From 81ca87f61dcd4f3e640370df7b4e3a98c3e92c92 Mon Sep 17 00:00:00 2001 From: PareekshithPalat Date: Tue, 7 Jul 2026 23:40:23 +0530 Subject: [PATCH 1/5] Added Optional --- .../repository/EncryptionKeyRepository.java | 4 ++- .../EncryptionKeyStoreServiceImpl.java | 32 ++++++++----------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/repository/EncryptionKeyRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/repository/EncryptionKeyRepository.java index fe8ad7bd32b..79f2e980c23 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/repository/EncryptionKeyRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/repository/EncryptionKeyRepository.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.infrastructure.crypt.repository; +import java.util.Optional; + import org.apache.fineract.infrastructure.crypt.domain.EncryptionKey; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -25,5 +27,5 @@ public interface EncryptionKeyRepository extends JpaRepository, JpaSpecificationExecutor { - EncryptionKey findByKeyType(String keyType); + Optional findByKeyType(String keyType); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/service/EncryptionKeyStoreServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/service/EncryptionKeyStoreServiceImpl.java index 1820a36201f..16f04f7bab0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/service/EncryptionKeyStoreServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/crypt/service/EncryptionKeyStoreServiceImpl.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.util.Base64; +import java.util.Optional; import jakarta.inject.Singleton; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; @@ -59,12 +60,12 @@ public EncryptionKeyStoreServiceImpl( private void storeKey(String type, EncryptionKeyPair key) { - EncryptionKey entity = encryptionKeyRepository.findByKeyType(type); - - if (entity == null) { - entity = new EncryptionKey(); - entity.setKeyType(type); - } + EncryptionKey entity = encryptionKeyRepository.findByKeyType(type) + .orElseGet(() -> { + EncryptionKey newEntity = new EncryptionKey(); + newEntity.setKeyType(type); + return newEntity; + }); entity.setPublicKey( Base64.getEncoder().encodeToString(key.getPublicKey())); @@ -101,18 +102,13 @@ private EncryptionKeyPair retrieveValidKey(String type){ private EncryptionKeyPair getKeys(String type) { - EncryptionKey entity = encryptionKeyRepository.findByKeyType(type); - - if (entity == null) { - return null; - } - - return new EncryptionKeyPair( - Base64.getDecoder().decode(entity.getPrivateKey()), - Base64.getDecoder().decode(entity.getPublicKey()), - entity.getCreatedAt(), - entity.getVersion() - ); + return encryptionKeyRepository.findByKeyType(type) + .map(entity -> new EncryptionKeyPair( + Base64.getDecoder().decode(entity.getPrivateKey()), + Base64.getDecoder().decode(entity.getPublicKey()), + entity.getCreatedAt(), + entity.getVersion())) + .orElse(null); } @Override From c5883476de125d4deee330db6367e824bd8bf5a3 Mon Sep 17 00:00:00 2001 From: PareekshithPalat Date: Sat, 11 Jul 2026 17:39:40 +0530 Subject: [PATCH 2/5] Pre-cleaned-Resolved-Localhost-Api-Hook-Fix --- .../core/config/FineractProperties.java | 11 ++ .../fineract/TempPasswordGenerator.java | 17 +++ .../service/TemplateMergeService.java | 102 +++++++++++++---- .../src/main/resources/application.properties | 4 + .../db/changelog/tenant/changelog-tenant.xml | 1 + .../parts/0136_add_template_system_user.xml | 103 ++++++++++++++++++ 6 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0136_add_template_system_user.xml diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java index 3eaf3437e19..209c40d4e35 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java @@ -394,6 +394,17 @@ public static class FineractTemplateProperties { private boolean regexWhitelistEnabled; private List regexWhitelist; + + private InternalUser internalUser; + + @Getter + @Setter + public static class InternalUser { + + private String username; + private String password; + + } } @Getter diff --git a/fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java new file mode 100644 index 00000000000..0277c45ced4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java @@ -0,0 +1,17 @@ +package org.apache.fineract; + +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; + +public class TempPasswordGenerator { + + public static void main(String[] args) { + + PasswordEncoder encoder = + PasswordEncoderFactories.createDelegatingPasswordEncoder(); + + String hash = encoder.encode("Template@123"); + + System.out.println(hash); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java b/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java index d8da3c1ddf8..4db8a49ccca 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java @@ -40,14 +40,15 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.Base64; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.core.config.FineractProperties; -import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.template.domain.Template; import org.apache.fineract.template.domain.TemplateFunctions; import org.apache.fineract.template.exception.TemplateForbiddenException; -import org.springframework.security.core.context.SecurityContextHolder; +import org.apache.fineract.infrastructure.core.config.FineractProperties.FineractTemplateProperties.InternalUser; @Slf4j @RequiredArgsConstructor @@ -103,7 +104,14 @@ private Map getCompiledMapFromMappers(final Map } if (!url.startsWith("http")) { log.info("Base URL : {}", scopes.get("BASE_URI")); - url = scopes.get("BASE_URI") + url; + String baseUrl = scopes.get("BASE_URI").toString(); + + if (baseUrl.endsWith("/") && url.startsWith("/")) { + url = baseUrl.substring(0, baseUrl.length() - 1) + url; + } else { + url = baseUrl + url; + } + log.info("Calling URL: {}", url); } try { scopes.put(entry.getKey(), getMapFromUrl(url)); @@ -117,16 +125,34 @@ private Map getCompiledMapFromMappers(final Map @SuppressWarnings("unchecked") private Map getMapFromUrl(final String url) throws IOException { + final HttpURLConnection connection = getConnection(url); - final String response = getStringFromInputStream(connection.getInputStream()); - HashMap result = new HashMap<>(); - if (connection.getContentType().equals("text/plain")) { - result.put("src", response); - } else { - result = new ObjectMapper().readValue(response, HashMap.class); + try { + + final String response = getStringFromInputStream(connection.getInputStream()); + + HashMap result = new HashMap<>(); + + if ("text/plain".equals(connection.getContentType())) { + result.put("src", response); + } else { + result = new ObjectMapper().readValue(response, HashMap.class); + } + + return result; + + } catch (IOException e) { + + log.error("HTTP Status : {}", connection.getResponseCode()); + log.error("URL : {}", url); + + if (connection.getErrorStream() != null) { + log.error("Error Body : {}", getStringFromInputStream(connection.getErrorStream())); + } + + throw e; } - return result; } private HttpURLConnection getConnection(final String url) { @@ -150,26 +176,56 @@ private HttpURLConnection getConnection(final String url) { } } - String authToken = ThreadLocalContextUtil.getAuthToken(); - if (authToken == null) { - final String name = SecurityContextHolder.getContext().getAuthentication().getName(); - final String password = SecurityContextHolder.getContext().getAuthentication().getCredentials().toString(); + final InternalUser internalUser = fineractProperties + .getTemplate() + .getInternalUser(); - Authenticator.setDefault(new Authenticator() { + final String name = internalUser.getUsername(); + final String password = internalUser.getPassword(); - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(name, password.toCharArray()); - } - }); - } + log.info("TemplateMergeService using internal user: {}", name); HttpURLConnection connection = null; try { connection = (HttpURLConnection) new URL(url).openConnection(); - if (authToken != null) { - connection.setRequestProperty("Authorization", fineractProperties.getSecurity().getBasicauth().getTokentype()+ " " + authToken);// NOSONAR + String credentials = name + ":" + password; + + String basicAuth = Base64.getEncoder() + .encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); + + if (ThreadLocalContextUtil.getTenant() == null) { + throw new IllegalStateException("Tenant context is missing"); } + + String tenantId = ThreadLocalContextUtil.getTenant().getTenantIdentifier(); + + log.info("Using tenant: {}", tenantId); + + connection.setRequestProperty( + "Authorization", + "Basic " + basicAuth); + + connection.setRequestProperty( + "Fineract-Platform-TenantId", + tenantId); + + connection.setRequestProperty( + "Accept", + "application/json"); + + connection.setRequestProperty( + "Content-Type", + "application/json"); + + log.info("Authorization Header: {}", connection.getRequestProperty("Authorization")); + log.info("Tenant Header: {}", connection.getRequestProperty("Fineract-Platform-TenantId")); + + connection.setRequestMethod("GET"); + log.info("Request Method: {}", connection.getRequestMethod()); + log.info("Tenant Header: {}", tenantId); + log.info("Authorization header configured."); + log.info("Connection created for URL: {}", url); + log.info("Connection created for URL: {}", url); TrustModifier.relaxHostChecking(connection); connection.setDoInput(true); diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index 3df16ee8908..c91c5bb7344 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -156,6 +156,10 @@ fineract.content.s3.secretKey=${FINERACT_CONTENT_S3_SECRET_KEY:} fineract.template.regex-whitelist-enabled=${FINERACT_TEMPLATE_REGEX_WHITELIST_ENABLED:true} fineract.template.regex-whitelist=${FINERACT_TEMPLATE_REGEX_WHITELIST:^\.+$} +# Internal service account used by TemplateMergeService +fineract.template.internal-user.username=${FINERACT_TEMPLATE_INTERNAL_USERNAME:template_system} +fineract.template.internal-user.password=${FINERACT_TEMPLATE_INTERNAL_PASSWORD:Template@123} + fineract.report.export.s3.bucket=${FINERACT_REPORT_EXPORT_S3_BUCKET_NAME:} fineract.report.export.s3.enabled=${FINERACT_REPORT_EXPORT_S3_ENABLED:false} diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 2deb0c3f060..d186121fac2 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -154,4 +154,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0136_add_template_system_user.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0136_add_template_system_user.xml new file mode 100644 index 00000000000..32da5faa0e3 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0136_add_template_system_user.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From abe61996a58a1d6414d11e9a56da9287dd5d34cd Mon Sep 17 00:00:00 2001 From: PareekshithPalat Date: Sat, 11 Jul 2026 18:52:06 +0530 Subject: [PATCH 3/5] Resolved Internal localhost hooks requests intermediate errors --- .../fineract/TempPasswordGenerator.java | 17 ------------- .../service/TemplateMergeService.java | 25 +++---------------- 2 files changed, 4 insertions(+), 38 deletions(-) delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java deleted file mode 100644 index 0277c45ced4..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/TempPasswordGenerator.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.apache.fineract; - -import org.springframework.security.crypto.factory.PasswordEncoderFactories; -import org.springframework.security.crypto.password.PasswordEncoder; - -public class TempPasswordGenerator { - - public static void main(String[] args) { - - PasswordEncoder encoder = - PasswordEncoderFactories.createDelegatingPasswordEncoder(); - - String hash = encoder.encode("Template@123"); - - System.out.println(hash); - } -} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java b/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java index 4db8a49ccca..e4eb27bfd86 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/template/service/TemplateMergeService.java @@ -28,9 +28,7 @@ import java.io.InputStreamReader; import java.io.StringReader; import java.io.StringWriter; -import java.net.Authenticator; import java.net.HttpURLConnection; -import java.net.PasswordAuthentication; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.KeyManagementException; @@ -103,7 +101,6 @@ private Map getCompiledMapFromMappers(final Map scopes.put("BASE_URI", fineractProperties.getBaseUrl()); } if (!url.startsWith("http")) { - log.info("Base URL : {}", scopes.get("BASE_URI")); String baseUrl = scopes.get("BASE_URI").toString(); if (baseUrl.endsWith("/") && url.startsWith("/")) { @@ -111,7 +108,6 @@ private Map getCompiledMapFromMappers(final Map } else { url = baseUrl + url; } - log.info("Calling URL: {}", url); } try { scopes.put(entry.getKey(), getMapFromUrl(url)); @@ -176,21 +172,18 @@ private HttpURLConnection getConnection(final String url) { } } - final InternalUser internalUser = fineractProperties - .getTemplate() - .getInternalUser(); + final InternalUser internalUser = + fineractProperties.getTemplate().getInternalUser(); final String name = internalUser.getUsername(); final String password = internalUser.getPassword(); - log.info("TemplateMergeService using internal user: {}", name); - HttpURLConnection connection = null; try { connection = (HttpURLConnection) new URL(url).openConnection(); - String credentials = name + ":" + password; + final String credentials = name + ":" + password; - String basicAuth = Base64.getEncoder() + final String basicAuth = Base64.getEncoder() .encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); if (ThreadLocalContextUtil.getTenant() == null) { @@ -199,8 +192,6 @@ private HttpURLConnection getConnection(final String url) { String tenantId = ThreadLocalContextUtil.getTenant().getTenantIdentifier(); - log.info("Using tenant: {}", tenantId); - connection.setRequestProperty( "Authorization", "Basic " + basicAuth); @@ -217,15 +208,7 @@ private HttpURLConnection getConnection(final String url) { "Content-Type", "application/json"); - log.info("Authorization Header: {}", connection.getRequestProperty("Authorization")); - log.info("Tenant Header: {}", connection.getRequestProperty("Fineract-Platform-TenantId")); - connection.setRequestMethod("GET"); - log.info("Request Method: {}", connection.getRequestMethod()); - log.info("Tenant Header: {}", tenantId); - log.info("Authorization header configured."); - log.info("Connection created for URL: {}", url); - log.info("Connection created for URL: {}", url); TrustModifier.relaxHostChecking(connection); connection.setDoInput(true); From b61bbaeca7be4b567809e4d82cce5f3deb294583 Mon Sep 17 00:00:00 2001 From: PareekshithPalat Date: Fri, 17 Jul 2026 20:22:02 +0530 Subject: [PATCH 4/5] Pre Push Fix --- .../GuarantorWritePlatformServiceJpaRepositoryIImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java index 375e83d156b..9a7befba0d4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java @@ -177,7 +177,7 @@ private CommandProcessingResult createGuarantor(final Loan loan, final JsonComma } this.guarantorRepository.saveAndFlush(guarantor); return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(guarantor.getOfficeId()) - .withEntityId(guarantor.getId()).withLoanId(loan.getId()).build(); + .withClientId(guarantor.getClientId()).withEntityId(guarantor.getId()).withLoanId(loan.getId()).build(); } catch (final JpaSystemException | DataIntegrityViolationException dve) { final Throwable throwable = dve.getMostSpecificCause(); handleGuarantorDataIntegrityIssues(throwable, dve); @@ -248,7 +248,7 @@ public CommandProcessingResult updateGuarantor(final Long loanId, final Long gua } return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(guarantorForUpdate.getOfficeId()) - .withEntityId(guarantorForUpdate.getId()).withOfficeId(guarantorForUpdate.getLoanId()).with(changesOnly).build(); + .withClientId(guarantorForUpdate.getClientId()).withEntityId(guarantorForUpdate.getId()).withOfficeId(guarantorForUpdate.getLoanId()).with(changesOnly).build(); } catch (final JpaSystemException | DataIntegrityViolationException dve) { final Throwable throwable = dve.getMostSpecificCause(); handleGuarantorDataIntegrityIssues(throwable, dve); From 42554acbed335390cc221d8e67779ee1fecb0887 Mon Sep 17 00:00:00 2001 From: PareekshithPalat Date: Wed, 22 Jul 2026 12:02:04 +0530 Subject: [PATCH 5/5] add guarantor approval and rejection workflow --- .../service/CommandWrapperBuilder.java | 18 +++++ .../guarantor/api/GuarantorsApiResource.java | 31 +++++++ .../guarantor/data/GuarantorData.java | 13 +-- .../guarantor/domain/Guarantor.java | 35 +++++++- .../domain/GuarantorApprovalStatus.java | 80 +++++++++++++++++++ .../GuarantorNotPendingException.java | 34 ++++++++ .../ApproveGuarantorCommandHandler.java | 49 ++++++++++++ .../RejectGuarantorCommandHandler.java | 49 ++++++++++++ .../GuarantorReadPlatformServiceImpl.java | 4 +- .../GuarantorWritePlatformService.java | 4 + ...ritePlatformServiceJpaRepositoryIImpl.java | 74 ++++++++++++++++- .../db/changelog/tenant/changelog-tenant.xml | 1 + .../0137_add_guarantor_approval_status.xml | 42 ++++++++++ 13 files changed, 423 insertions(+), 11 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/GuarantorApprovalStatus.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/exception/GuarantorNotPendingException.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/ApproveGuarantorCommandHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/RejectGuarantorCommandHandler.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0137_add_guarantor_approval_status.xml diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 89dfc9680de..70303f8dc84 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -355,6 +355,24 @@ public CommandWrapperBuilder updateGuarantor(final Long loanId, final Long guara return this; } + public CommandWrapperBuilder approveGuarantor(final Long loanId, final Long guarantorId) { + this.actionName = "APPROVE"; + this.entityName = "GUARANTOR"; + this.entityId = guarantorId; + this.loanId = loanId; + this.href = "/loans/" + loanId + "/guarantors/" + guarantorId + "?command=approve"; + return this; + } + + public CommandWrapperBuilder rejectGuarantor(final Long loanId, final Long guarantorId) { + this.actionName = "REJECT"; + this.entityName = "GUARANTOR"; + this.entityId = guarantorId; + this.loanId = loanId; + this.href = "/loans/" + loanId + "/guarantors/" + guarantorId + "?command=reject"; + return this; + } + public CommandWrapperBuilder deleteGuarantor(final Long loanId, final Long guarantorId, final Long guarantorFundingId) { this.actionName = "DELETE"; this.entityName = "GUARANTOR"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/api/GuarantorsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/api/GuarantorsApiResource.java index 6843d679171..2e68ae95bf0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/api/GuarantorsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/api/GuarantorsApiResource.java @@ -181,6 +181,37 @@ public String updateGuarantor(@PathParam("loanId") final Long loanId, @PathParam return this.apiJsonSerializerService.serialize(result); } + @POST + @Path("{guarantorId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String guarantorCommand(@PathParam("loanId") final Long loanId, + @PathParam("guarantorId") final Long guarantorId, + @QueryParam("command") final String command, + final String jsonRequestBody) { + + CommandWrapper commandRequest = null; + + if ("approve".equalsIgnoreCase(command)) { + commandRequest = new CommandWrapperBuilder() + .approveGuarantor(loanId, guarantorId) + .withJson(jsonRequestBody) + .build(); + } else if ("reject".equalsIgnoreCase(command)) { + commandRequest = new CommandWrapperBuilder() + .rejectGuarantor(loanId, guarantorId) + .withJson(jsonRequestBody) + .build(); + } else { + throw new IllegalArgumentException("Unsupported command: " + command); + } + + final CommandProcessingResult result = + this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.apiJsonSerializerService.serialize(result); + } + @DELETE @Path("{guarantorId}") @Consumes({ MediaType.APPLICATION_JSON }) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/GuarantorData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/GuarantorData.java index f4cf7a987a0..692d0f3bab1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/GuarantorData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/GuarantorData.java @@ -62,6 +62,7 @@ public class GuarantorData { private final LocalDate dob; private final Collection guarantorFundingDetails; private final boolean status; + private final Integer approvalStatus; // template @SuppressWarnings("unused") @@ -119,6 +120,7 @@ private GuarantorData(Integer guarantorTypeId, Integer clientRelationshipTypeId, this.comment = null; this.guarantorFundingDetails = null; this.status = false; + this.approvalStatus = null; this.guarantorTypeOptions = null; this.allowedClientRelationshipTypes = null; this.accountLinkingOptions = null; @@ -129,7 +131,7 @@ public static GuarantorData template(final List guarantorTypeOpt final Collection guarantorFundingDetails = null; final boolean status = false; return new GuarantorData(null, null, null, null, GuarantorEnumerations.guarantorType(GuarantorType.CUSTOMER), null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, status, guarantorFundingDetails, + null, null, null, null, null, null, null, null, null, null, null, null, status, null, guarantorFundingDetails, guarantorTypeOptions, allowedClientRelationshipTypes, accountLinkingOptions); } @@ -139,7 +141,7 @@ public static GuarantorData templateOnTop(final GuarantorData guarantorData, fin guarantorData.guarantorType, guarantorData.firstname, guarantorData.lastname, guarantorData.dob, guarantorData.addressLine1, guarantorData.addressLine2, guarantorData.city, guarantorData.state, guarantorData.zip, guarantorData.country, guarantorData.mobileNumber, guarantorData.housePhoneNumber, guarantorData.comment, guarantorData.officeName, - guarantorData.joinedDate, guarantorData.externalId, guarantorData.status, guarantorData.guarantorFundingDetails, + guarantorData.joinedDate, guarantorData.externalId, guarantorData.status, guarantorData.approvalStatus, guarantorData.guarantorFundingDetails, guarantorTypeOptions, allowedClientRelationshipTypes, accountLinkingOptions); } @@ -147,14 +149,14 @@ public static GuarantorData mergeClientData(final ClientData clientData, final G return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId, guarantorData.guarantorType, clientData.getFirstname(), clientData.getLastname(), null, null, null, null, null, null, null, null, null, null, clientData.getOfficeName(), clientData.getActivationDate(), clientData.getExternalId().getValue(), - guarantorData.status, guarantorData.guarantorFundingDetails, null, guarantorData.allowedClientRelationshipTypes, + guarantorData.status, guarantorData.approvalStatus, guarantorData.guarantorFundingDetails, null, guarantorData.allowedClientRelationshipTypes, guarantorData.accountLinkingOptions); } public static GuarantorData mergeStaffData(final StaffData staffData, final GuarantorData guarantorData) { return new GuarantorData(guarantorData.id, guarantorData.loanId, guarantorData.clientRelationshipType, guarantorData.entityId, guarantorData.guarantorType, staffData.getFirstname(), staffData.getLastname(), null, null, null, null, null, null, null, - null, null, null, staffData.getOfficeName(), null, null, guarantorData.status, guarantorData.guarantorFundingDetails, null, + null, null, null, staffData.getOfficeName(), null, null, guarantorData.status, guarantorData.approvalStatus, guarantorData.guarantorFundingDetails, null, guarantorData.allowedClientRelationshipTypes, guarantorData.accountLinkingOptions); } @@ -162,7 +164,7 @@ public GuarantorData(final Long id, final Long loanId, final CodeValueData clien final EnumOptionData guarantorType, final String firstname, final String lastname, final LocalDate dob, final String addressLine1, final String addressLine2, final String city, final String state, final String zip, final String country, final String mobileNumber, final String housePhoneNumber, final String comment, final String officeName, - final LocalDate joinedDate, final String externalId, final boolean status, + final LocalDate joinedDate, final String externalId, final boolean status, final Integer approvalStatus, Collection guarantorFundingDetails, final List guarantorTypeOptions, final Collection allowedClientRelationshipTypes, final Collection accountLinkingOptions) { this.id = id; @@ -186,6 +188,7 @@ public GuarantorData(final Long id, final Long loanId, final CodeValueData clien this.joinedDate = joinedDate; this.externalId = externalId; this.status = status; + this.approvalStatus = approvalStatus; this.guarantorFundingDetails = guarantorFundingDetails; this.guarantorTypeOptions = guarantorTypeOptions; this.allowedClientRelationshipTypes = allowedClientRelationshipTypes; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/Guarantor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/Guarantor.java index fc020b5dffc..b72754d66a6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/Guarantor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/Guarantor.java @@ -95,6 +95,9 @@ public class Guarantor extends AbstractPersistableCustom { @Column(name = "is_active", nullable = false) private boolean active; + @Column(name = "approval_status", nullable = false) + private Integer approvalStatus; + @OneToMany(cascade = CascadeType.ALL, mappedBy = "guarantor", orphanRemoval = true, fetch = FetchType.EAGER) private List guarantorFundDetails = new ArrayList<>(); @@ -106,7 +109,7 @@ private Guarantor(final Loan loan, final CodeValue clientRelationshipType, final final String firstname, final String lastname, final LocalDate dateOfBirth, final String addressLine1, final String addressLine2, final String city, final String state, final String country, final String zip, final String housePhoneNumber, final String mobilePhoneNumber, final String comment, final boolean active, - final List guarantorFundDetails) { + final Integer approvalStatus,final List guarantorFundDetails) { this.loan = loan; this.clientRelationshipType = clientRelationshipType; this.gurantorType = gurantorType; @@ -124,6 +127,7 @@ private Guarantor(final Loan loan, final CodeValue clientRelationshipType, final this.mobilePhoneNumber = StringUtils.defaultIfEmpty(mobilePhoneNumber, null); this.comment = StringUtils.defaultIfEmpty(comment, null); this.active = active; + this.approvalStatus = approvalStatus; this.guarantorFundDetails.addAll(guarantorFundDetails); } @@ -132,6 +136,7 @@ public static Guarantor fromJson(final Loan loan, final CodeValue clientRelation final Integer gurantorType = command.integerValueSansLocaleOfParameterNamed(GuarantorJSONinputParams.GUARANTOR_TYPE_ID.getValue()); final Long entityId = command.longValueOfParameterNamed(GuarantorJSONinputParams.ENTITY_ID.getValue()); final boolean active = true; + final Integer approvalStatus = GuarantorApprovalStatus.PENDING.getValue(); if (GuarantorType.EXTERNAL.getValue().equals(gurantorType)) { final String firstname = command.stringValueOfParameterNamed(GuarantorJSONinputParams.FIRSTNAME.getValue()); final String lastname = command.stringValueOfParameterNamed(GuarantorJSONinputParams.LASTNAME.getValue()); @@ -147,11 +152,11 @@ public static Guarantor fromJson(final Loan loan, final CodeValue clientRelation final String comment = command.stringValueOfParameterNamed(GuarantorJSONinputParams.COMMENT.getValue()); return new Guarantor(loan, clientRelationshipType, gurantorType, entityId, firstname, lastname, dateOfBirth, addressLine1, - addressLine2, city, state, country, zip, housePhoneNumber, mobilePhoneNumber, comment, active, fundingDetails); + addressLine2, city, state, country, zip, housePhoneNumber, mobilePhoneNumber, comment, active, approvalStatus, fundingDetails); } return new Guarantor(loan, clientRelationshipType, gurantorType, entityId, null, null, null, null, null, null, null, null, null, - null, null, null, active, fundingDetails); + null, null, null, active, approvalStatus, fundingDetails); } @@ -299,6 +304,30 @@ public void updateStatus(final boolean status) { this.active = status; } + public void approve() { + this.approvalStatus = GuarantorApprovalStatus.APPROVED.getValue(); + } + + public void reject() { + this.approvalStatus = GuarantorApprovalStatus.REJECTED.getValue(); + } + + public boolean isPending() { + return GuarantorApprovalStatus.fromInt(this.approvalStatus).isPending(); + } + + public boolean isApproved() { + return GuarantorApprovalStatus.fromInt(this.approvalStatus).isApproved(); + } + + public boolean isRejected() { + return GuarantorApprovalStatus.fromInt(this.approvalStatus).isRejected(); + } + + public Integer getApprovalStatus() { + return this.approvalStatus; + } + public void addFundingDetails(final List fundingDetails) { this.guarantorFundDetails.addAll(fundingDetails); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/GuarantorApprovalStatus.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/GuarantorApprovalStatus.java new file mode 100644 index 00000000000..003e615bdbe --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/domain/GuarantorApprovalStatus.java @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanaccount.guarantor.domain; + +/** + * Enum representation of {@link Guarantor} approval status. + */ +public enum GuarantorApprovalStatus { + + INVALID(0, "guarantorApprovalStatus.invalid"), // + PENDING(100, "guarantorApprovalStatus.pending"), // + APPROVED(200, "guarantorApprovalStatus.approved"), // + REJECTED(300, "guarantorApprovalStatus.rejected"); + + private final Integer value; + private final String code; + + public static GuarantorApprovalStatus fromInt(final Integer type) { + + GuarantorApprovalStatus enumeration = GuarantorApprovalStatus.INVALID; + switch (type) { + case 100: + enumeration = GuarantorApprovalStatus.PENDING; + break; + case 200: + enumeration = GuarantorApprovalStatus.APPROVED; + break; + case 300: + enumeration = GuarantorApprovalStatus.REJECTED; + break; + } + + return enumeration; + } + + GuarantorApprovalStatus(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public boolean hasStateOf(final GuarantorApprovalStatus state) { + return this.value.equals(state.getValue()); + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isPending() { + return this.value.equals(GuarantorApprovalStatus.PENDING.getValue()); + } + + public boolean isApproved() { + return this.value.equals(GuarantorApprovalStatus.APPROVED.getValue()); + } + + public boolean isRejected() { + return this.value.equals(GuarantorApprovalStatus.REJECTED.getValue()); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/exception/GuarantorNotPendingException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/exception/GuarantorNotPendingException.java new file mode 100644 index 00000000000..8eba645b59b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/exception/GuarantorNotPendingException.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanaccount.guarantor.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * Thrown when attempting to approve or reject a guarantor that is not in the PENDING state. + */ +public class GuarantorNotPendingException extends AbstractPlatformDomainRuleException { + + public GuarantorNotPendingException(final Long guarantorId) { + super( + "error.msg.guarantor.is.not.pending", + "Guarantor with id " + guarantorId + " cannot be approved or rejected because it is no longer in the PENDING state.", + guarantorId); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/ApproveGuarantorCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/ApproveGuarantorCommandHandler.java new file mode 100644 index 00000000000..34f3113dd3d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/ApproveGuarantorCommandHandler.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.portfolio.loanaccount.guarantor.handler; + +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = "GUARANTOR", action = "APPROVE") +public class ApproveGuarantorCommandHandler implements NewCommandSourceHandler { + + private final GuarantorWritePlatformService writePlatformService; + + @Autowired + public ApproveGuarantorCommandHandler(final GuarantorWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.approveGuarantor( + command.getLoanId(), + command.entityId()); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/RejectGuarantorCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/RejectGuarantorCommandHandler.java new file mode 100644 index 00000000000..662cc7f1133 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/handler/RejectGuarantorCommandHandler.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.portfolio.loanaccount.guarantor.handler; + +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = "GUARANTOR", action = "REJECT") +public class RejectGuarantorCommandHandler implements NewCommandSourceHandler { + + private final GuarantorWritePlatformService writePlatformService; + + @Autowired + public RejectGuarantorCommandHandler(final GuarantorWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.rejectGuarantor( + command.getLoanId(), + command.entityId()); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java index 14828eabffd..7622a9dca4f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java @@ -121,6 +121,7 @@ private static final class GuarantorMapper implements RowMapper { private final StringBuilder sqlBuilder = new StringBuilder( " g.id as id, g.loan_id as loanId, g.client_reln_cv_id clientRelationshipTypeId, g.entity_id as entityId, g.type_enum guarantorType ,g.firstname as firstname, g.lastname as lastname, g.dob as dateOfBirth, g.address_line_1 as addressLine1, g.address_line_2 as addressLine2, g.city as city, g.state as state, g.country as country, g.zip as zip, g.house_phone_number as housePhoneNumber, g.mobile_number as mobilePhoneNumber, g.comment as comment, ") .append(" g.is_active as guarantorStatus,")// + .append(" g.approval_status as approvalStatus,")// .append(" cv.code_value as typeName, ")// .append("gfd.amount,")// .append(this.guarantorFundingMapper.schema())// @@ -166,6 +167,7 @@ public GuarantorData mapRow(final ResultSet rs, final int rowNum) throws SQLExce final String housePhoneNumber = rs.getString("housePhoneNumber"); final String comment = rs.getString("comment"); final boolean status = rs.getBoolean("guarantorStatus"); + final Integer approvalStatus = rs.getInt("approvalStatus"); final Collection accountLinkingOptions = null; List guarantorFundingDetails = null; GuarantorFundingData guarantorFundingData = this.guarantorFundingMapper.mapRow(rs, rowNum); @@ -187,7 +189,7 @@ public GuarantorData mapRow(final ResultSet rs, final int rowNum) throws SQLExce return new GuarantorData(id, loanId, clientRelationshipType, entityId, guarantorType, firstname, lastname, dob, addressLine1, addressLine2, city, state, zip, country, mobileNumber, housePhoneNumber, comment, null, null, null, status, - guarantorFundingDetails, null, null, accountLinkingOptions); + approvalStatus, guarantorFundingDetails, null, null, accountLinkingOptions); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformService.java index f545a26c53e..e6380bc82e4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformService.java @@ -27,6 +27,10 @@ public interface GuarantorWritePlatformService { CommandProcessingResult updateGuarantor(Long loanId, Long guarantorId, JsonCommand command); + CommandProcessingResult approveGuarantor(Long loanId, Long guarantorId); + + CommandProcessingResult rejectGuarantor(Long loanId, Long guarantorId); + CommandProcessingResult removeGuarantor(Long loanId, Long guarantorId, Long guarantorFundingId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java index 9a7befba0d4..2ece32025b7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorWritePlatformServiceJpaRepositoryIImpl.java @@ -50,6 +50,7 @@ import org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorType; import org.apache.fineract.portfolio.loanaccount.guarantor.exception.DuplicateGuarantorException; import org.apache.fineract.portfolio.loanaccount.guarantor.exception.GuarantorNotFoundException; +import org.apache.fineract.portfolio.loanaccount.guarantor.exception.GuarantorNotPendingException; import org.apache.fineract.portfolio.loanaccount.guarantor.exception.InvalidGuarantorException; import org.apache.fineract.portfolio.loanaccount.guarantor.serialization.GuarantorCommandFromApiJsonDeserializer; import org.apache.fineract.portfolio.savings.domain.SavingsAccount; @@ -124,8 +125,8 @@ private CommandProcessingResult createGuarantor(final Loan loan, final JsonComma guarantorFundingDetails.add(fundingDetails); if (loan.isDisbursed() || (loan.isApproved() && (loan.getGuaranteeAmount() != null || loan.loanProduct().isHoldGuaranteeFundsEnabled()))) { - this.guarantorDomainService.assignGuarantor(fundingDetails, DateUtils.getBusinessLocalDate()); - loan.updateGuaranteeAmount(fundingDetails.getAmount()); + // this.guarantorDomainService.assignGuarantor(fundingDetails, DateUtils.getBusinessLocalDate()); + //loan.updateGuaranteeAmount(fundingDetails.getAmount()); } } @@ -256,6 +257,75 @@ public CommandProcessingResult updateGuarantor(final Long loanId, final Long gua } } + @Override + @Transactional + public CommandProcessingResult approveGuarantor(final Long loanId, final Long guarantorId) { + + final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true); + + validateLoanStatus(loan); + + final Guarantor guarantor = this.guarantorRepository.findByLoanAndId(loan, guarantorId); + + if (guarantor == null) { + throw new GuarantorNotFoundException(loanId, guarantorId); + } + + if (!guarantor.isPending()) { + throw new GuarantorNotPendingException(guarantor.getId()); + } + + guarantor.approve(); + + for (GuarantorFundingDetails fundingDetails : guarantor.getGuarantorFundDetails()) { + + this.guarantorDomainService.assignGuarantor( + fundingDetails, + DateUtils.getBusinessLocalDate()); + + loan.updateGuaranteeAmount(fundingDetails.getAmount()); + } + + this.guarantorRepository.saveAndFlush(guarantor); + + return new CommandProcessingResultBuilder() + .withEntityId(guarantor.getId()) + .withLoanId(loan.getId()) + .withOfficeId(guarantor.getOfficeId()) + .withClientId(guarantor.getClientId()) + .build(); + } + + @Override + @Transactional + public CommandProcessingResult rejectGuarantor(final Long loanId, final Long guarantorId) { + + final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true); + + validateLoanStatus(loan); + + final Guarantor guarantor = this.guarantorRepository.findByLoanAndId(loan, guarantorId); + + if (guarantor == null) { + throw new GuarantorNotFoundException(loanId, guarantorId); + } + + if (!guarantor.isPending()) { + throw new GuarantorNotPendingException(guarantor.getId()); + } + + guarantor.reject(); + + this.guarantorRepository.saveAndFlush(guarantor); + + return new CommandProcessingResultBuilder() + .withEntityId(guarantor.getId()) + .withLoanId(loan.getId()) + .withOfficeId(guarantor.getOfficeId()) + .withClientId(guarantor.getClientId()) + .build(); + } + @Override @Transactional public CommandProcessingResult removeGuarantor(final Long loanId, final Long guarantorId, final Long guarantorFundingId) { diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index d186121fac2..f12497a1006 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -155,4 +155,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0137_add_guarantor_approval_status.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0137_add_guarantor_approval_status.xml new file mode 100644 index 00000000000..f893918fa08 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0137_add_guarantor_approval_status.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file