Fix to use notes in Savings Accounts (#2857)

Co-authored-by: Jose Alberto Hernandez <alberto@MacBook-Pro.local>
This commit is contained in:
José Alberto Hernández
2023-01-06 14:17:35 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent 0e391edf47
commit a25acf0abd
7 changed files with 129 additions and 80 deletions
@@ -71,8 +71,8 @@ public class NotesApiResource {
public static final String GROUPNOTE = "GROUPNOTE"; public static final String GROUPNOTE = "GROUPNOTE";
public static final String INVALIDNOTE = "INVALIDNOTE"; public static final String INVALIDNOTE = "INVALIDNOTE";
private static final Set<String> NOTE_DATA_PARAMETERS = new HashSet<>( private static final Set<String> NOTE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("id", "clientId", "groupId", "loanId", "loanTransactionId", "depositAccountId", "savingAccountId", "noteType", Arrays.asList("id", "resourceId", "clientId", "groupId", "loanId", "loanTransactionId", "depositAccountId", "savingAccountId",
"note", "createdById", "createdByUsername", "createdOn", "updatedById", "updatedByUsername", "updatedOn")); "noteType", "note", "createdById", "createdByUsername", "createdOn", "updatedById", "updatedByUsername", "updatedOn"));
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final NoteReadPlatformService readPlatformService; private final NoteReadPlatformService readPlatformService;
private final DefaultToApiJsonSerializer<NoteData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<NoteData> toApiJsonSerializer;
@@ -45,5 +45,5 @@ public interface NoteRepository extends JpaRepository<Note, Long>, JpaSpecificat
List<Note> findBySavingsAccount(SavingsAccount savingAccount); List<Note> findBySavingsAccount(SavingsAccount savingAccount);
// Note findBySavingsAccountIdAndId(Long savingAccountId, Long id); Note findBySavingsAccountAndId(SavingsAccount savingAccount, Long id);
} }
@@ -132,7 +132,7 @@ public class NoteReadPlatformServiceImpl implements NoteReadPlatformService {
case SAVING_ACCOUNT: case SAVING_ACCOUNT:
paramList.add(NoteType.SAVING_ACCOUNT.getValue()); paramList.add(NoteType.SAVING_ACCOUNT.getValue());
paramList.add(NoteType.SAVINGS_TRANSACTION.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; break;
case SAVINGS_TRANSACTION: case SAVINGS_TRANSACTION:
conditionSql = " n.savings_account_transaction_id = ? "; conditionSql = " n.savings_account_transaction_id = ? ";
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.note.service; package org.apache.fineract.portfolio.note.service;
import java.util.Map; import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
@@ -40,28 +41,32 @@ import org.apache.fineract.portfolio.note.domain.NoteType;
import org.apache.fineract.portfolio.note.exception.NoteNotFoundException; import org.apache.fineract.portfolio.note.exception.NoteNotFoundException;
import org.apache.fineract.portfolio.note.exception.NoteResourceNotSupportedException; import org.apache.fineract.portfolio.note.exception.NoteResourceNotSupportedException;
import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer; import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer;
import org.slf4j.Logger; import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.slf4j.LoggerFactory; import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
@Slf4j
public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatformService { public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatformService {
private static final Logger LOG = LoggerFactory.getLogger(NoteWritePlatformServiceJpaRepositoryImpl.class);
private final NoteRepository noteRepository; private final NoteRepository noteRepository;
private final ClientRepositoryWrapper clientRepository; private final ClientRepositoryWrapper clientRepository;
private final GroupRepository groupRepository; private final GroupRepository groupRepository;
private final LoanRepositoryWrapper loanRepository; private final LoanRepositoryWrapper loanRepository;
private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionRepository loanTransactionRepository;
private final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final SavingsAccountRepository savingsAccountRepository;
public NoteWritePlatformServiceJpaRepositoryImpl(final NoteRepository noteRepository, final ClientRepositoryWrapper clientRepository, public NoteWritePlatformServiceJpaRepositoryImpl(final NoteRepository noteRepository, final ClientRepositoryWrapper clientRepository,
final GroupRepository groupRepository, final LoanRepositoryWrapper loanRepository, 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.noteRepository = noteRepository;
this.clientRepository = clientRepository; this.clientRepository = clientRepository;
this.groupRepository = groupRepository; this.groupRepository = groupRepository;
this.loanRepository = loanRepository; this.loanRepository = loanRepository;
this.loanTransactionRepository = loanTransactionRepository; this.loanTransactionRepository = loanTransactionRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.savingsAccountRepository = savingsAccountRepository;
} }
private CommandProcessingResult createClientNote(final JsonCommand command) { private CommandProcessingResult createClientNote(final JsonCommand command) {
@@ -151,26 +156,23 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
.build(); .build();
} }
// private CommandProcessingResult createSavingAccountNote(final JsonCommand private CommandProcessingResult createSavingAccountNote(final JsonCommand command) {
// command) { final Long resourceId = command.getSavingsId();
// final SavingsAccount savingAccount = this.savingsAccountRepository.findById(resourceId)
// final Long resourceId = command.getSupportedEntityId(); .orElseThrow(() -> new SavingsAccountNotFoundException(resourceId));
//
// final SavingAccount savingAccount = final String note = command.stringValueOfParameterNamed("note");
// this.savingAccountRepository.findOne(resourceId); final Note newNote = Note.savingNote(savingAccount, note);
// if (savingAccount == null) { throw new
// SavingAccountNotFoundException(resourceId); } this.noteRepository.saveAndFlush(newNote);
//
// final String note = command.stringValueOfParameterNamed("note"); return new CommandProcessingResultBuilder() //
// final Note newNote = Note.savingNote(savingAccount, note); .withCommandId(command.commandId()) //
// .withEntityId(newNote.getId()) //
// this.noteRepository.save(newNote); .withOfficeId(savingAccount.getClient().getOffice().getId()) //
// .withSavingsId(savingAccount.getId()) //
// return new CommandProcessingResultBuilder() // .build();
// .withCommandId(command.commandId()) // }
// .withEntityId(newNote.getId()) //
// .withClientId(savingAccount.getClient().getId()).withOfficeId(savingAccount.getClient().getOffice().getId()).build();
// }
@Override @Override
public CommandProcessingResult createNote(final JsonCommand command) { public CommandProcessingResult createNote(final JsonCommand command) {
@@ -192,9 +194,9 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
case LOAN_TRANSACTION: { case LOAN_TRANSACTION: {
return createLoanTransactionNote(command); return createLoanTransactionNote(command);
} }
// case SAVING_ACCOUNT: { case SAVING_ACCOUNT: {
// return createSavingAccountNote(command); return createSavingAccountNote(command);
// } }
default: default:
throw new NoteResourceNotSupportedException(resourceUrl); throw new NoteResourceNotSupportedException(resourceUrl);
} }
@@ -333,41 +335,30 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
.withLoanId(loan.getId()).withOfficeId(loan.getOfficeId()).with(changes).build(); .withLoanId(loan.getId()).withOfficeId(loan.getOfficeId()).with(changes).build();
} }
// private CommandProcessingResult updateSavingAccountNote(final JsonCommand private CommandProcessingResult updateSavingAccountNote(final JsonCommand command) {
// command) { final Long resourceId = command.getSavingsId();
// final Long noteId = command.entityId();
// final Long resourceId = command.getSupportedEntityId(); final NoteType type = NoteType.SAVING_ACCOUNT;
// final Long noteId = command.entityId(); final SavingsAccount savingAccount = this.savingsAccountRepository.findById(resourceId)
// final String resourceUrl = command.getSupportedEntityType(); .orElseThrow(() -> new SavingsAccountNotFoundException(resourceId));
//
// final NoteType type = NoteType.fromApiUrl(resourceUrl); final Note noteForUpdate = this.noteRepository.findBySavingsAccountAndId(savingAccount, noteId);
// if (noteForUpdate == null) {
// final SavingAccount savingAccount = throw new NoteNotFoundException(noteId, resourceId, type.name().toLowerCase());
// this.savingAccountRepository.findOne(resourceId); }
// if (savingAccount == null) { throw new final Map<String, Object> changes = noteForUpdate.update(command);
// SavingAccountNotFoundException(resourceId); } if (!changes.isEmpty()) {
// this.noteRepository.saveAndFlush(noteForUpdate);
// final Note noteForUpdate = }
// this.noteRepository.findBySavingAccountIdAndId(resourceId, noteId);
// return new CommandProcessingResultBuilder() //
// if (noteForUpdate == null) { throw new NoteNotFoundException(noteId, .withCommandId(command.commandId()) //
// resourceId, type.name().toLowerCase()); } .withEntityId(noteForUpdate.getId()) //
// .withOfficeId(savingAccount.getClient().getOffice().getId()) //
// final Map<String, Object> changes = noteForUpdate.update(command); .withSavingsId(savingAccount.getId()) //
// .with(changes) //
// if (!changes.isEmpty()) { .build();
// 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();
// }
@Override @Override
public CommandProcessingResult updateNote(final JsonCommand command) { public CommandProcessingResult updateNote(final JsonCommand command) {
@@ -390,9 +381,9 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
case LOAN_TRANSACTION: { case LOAN_TRANSACTION: {
return updateLoanTransactionNote(command); return updateLoanTransactionNote(command);
} }
// case SAVING_ACCOUNT: { case SAVING_ACCOUNT: {
// return updateSavingAccountNote(command); return updateSavingAccountNote(command);
// } }
default: default:
throw new NoteResourceNotSupportedException(resourceUrl); throw new NoteResourceNotSupportedException(resourceUrl);
} }
@@ -444,19 +435,19 @@ public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatf
noteForUpdate = this.noteRepository.findByLoanTransactionAndId(loanTransaction, noteId); noteForUpdate = this.noteRepository.findByLoanTransactionAndId(loanTransaction, noteId);
} }
break; break;
// case SAVING_ACCOUNT: { case SAVING_ACCOUNT: {
// noteForUpdate = final Long savinsAccountId = command.getSavingsId();
// this.noteRepository.findBySavingAccountIdAndId(resourceId, final SavingsAccount savingAccount = this.savingsAccountRepository.findById(savinsAccountId)
// noteId); .orElseThrow(() -> new SavingsAccountNotFoundException(savinsAccountId));
// }
// break; noteForUpdate = this.noteRepository.findBySavingsAccountAndId(savingAccount, noteId);
case SAVING_ACCOUNT: }
break; break;
case SHARE_ACCOUNT: case SHARE_ACCOUNT:
LOG.error("TODO Implement getNoteForDelete for SHARE_ACCOUNT"); log.error("TODO Implement getNoteForDelete for SHARE_ACCOUNT");
break; break;
case SAVINGS_TRANSACTION: case SAVINGS_TRANSACTION:
LOG.error("TODO Implement getNoteForDelete for SAVINGS_TRANSACTION"); log.error("TODO Implement getNoteForDelete for SAVINGS_TRANSACTION");
break; break;
} }
if (noteForUpdate == null) { if (noteForUpdate == null) {
@@ -28,6 +28,7 @@ import org.apache.fineract.portfolio.note.service.NoteReadPlatformService;
import org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl; import org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl;
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService; import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
import org.apache.fineract.portfolio.note.service.NoteWritePlatformServiceJpaRepositoryImpl; 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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -46,8 +47,8 @@ public class NoteAutoConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
public NoteWritePlatformService noteWritePlatformService(NoteRepository noteRepository, ClientRepositoryWrapper clientRepository, public NoteWritePlatformService noteWritePlatformService(NoteRepository noteRepository, ClientRepositoryWrapper clientRepository,
GroupRepository groupRepository, LoanRepositoryWrapper loanRepository, LoanTransactionRepository loanTransactionRepository, GroupRepository groupRepository, LoanRepositoryWrapper loanRepository, LoanTransactionRepository loanTransactionRepository,
NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer) { NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer, SavingsAccountRepository savingsAccountRepository) {
return new NoteWritePlatformServiceJpaRepositoryImpl(noteRepository, clientRepository, groupRepository, loanRepository, return new NoteWritePlatformServiceJpaRepositoryImpl(noteRepository, clientRepository, groupRepository, loanRepository,
loanTransactionRepository, fromApiJsonDeserializer); loanTransactionRepository, fromApiJsonDeserializer, savingsAccountRepository);
} }
} }
@@ -28,6 +28,8 @@ import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; 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.ClientHelper;
import org.apache.fineract.integrationtests.common.CollateralManagementHelper; import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
import org.apache.fineract.integrationtests.common.GroupHelper; import org.apache.fineract.integrationtests.common.GroupHelper;
@@ -36,6 +38,8 @@ import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; 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.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -47,6 +51,8 @@ public class NotesTest {
private RequestSpecification requestSpec; private RequestSpecification requestSpec;
private ResponseSpecification responseSpec404; private ResponseSpecification responseSpec404;
private LoanTransactionHelper loanTransactionHelper; private LoanTransactionHelper loanTransactionHelper;
private SavingsProductHelper savingsProductHelper;
private SavingsAccountHelper savingsAccountHelper;
@BeforeEach @BeforeEach
public void setup() { public void setup() {
@@ -56,6 +62,8 @@ public class NotesTest {
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.responseSpec404 = new ResponseSpecBuilder().expectStatusCode(404).build(); this.responseSpec404 = new ResponseSpecBuilder().expectStatusCode(404).build();
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
this.savingsProductHelper = new SavingsProductHelper();
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
} }
@Test @Test
@@ -190,6 +198,33 @@ public class NotesTest {
} }
@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) { private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID) {
List<HashMap> collaterals = new ArrayList<>(); List<HashMap> collaterals = new ArrayList<>();
final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec); final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
@@ -18,12 +18,18 @@
*/ */
package org.apache.fineract.integrationtests.common; package org.apache.fineract.integrationtests.common;
import com.google.gson.Gson;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; 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" }) @SuppressWarnings({ "rawtypes", "unchecked" })
public final class NotesHelper { public final class NotesHelper {
private static final Gson GSON = new JSON().getGson();
private NotesHelper() { private NotesHelper() {
} }
@@ -134,4 +140,20 @@ public final class NotesHelper {
Utils.performServerDelete(requestSpec, responseSpec, deleteLoanTransactionNoteURL, ""); 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);
}
} }