Compare commits
2
Commits
7c15fbb0c7
...
b1cfa996b4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1cfa996b4 | ||
|
|
02541eed6c |
+9
@@ -18,7 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.sanctions.domain;
|
package org.apache.fineract.portfolio.sanctions.domain;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
@@ -40,4 +42,11 @@ public interface SanctionsEntryRepository extends JpaRepository<SanctionsEntry,
|
|||||||
void deleteAllByActiveTrue();
|
void deleteAllByActiveTrue();
|
||||||
|
|
||||||
long countByActiveTrue();
|
long countByActiveTrue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp the active sanctions list was last loaded into the database — i.e. the data
|
||||||
|
* currency shown to callers so they know which list snapshot a screening ran against.
|
||||||
|
*/
|
||||||
|
@Query("SELECT MAX(s.createdDate) FROM SanctionsEntry s WHERE s.active = true")
|
||||||
|
Optional<OffsetDateTime> findListLastLoadedOn();
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-5
@@ -44,6 +44,7 @@ import java.time.LocalDate;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.commands.data.AuditData;
|
import org.apache.fineract.commands.data.AuditData;
|
||||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
import org.apache.fineract.commands.service.AuditReadPlatformService;
|
import org.apache.fineract.commands.service.AuditReadPlatformService;
|
||||||
@@ -142,7 +143,8 @@ public class ClientsApiResource {
|
|||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
@Operation(summary = "List Clients", description = "The list capability of clients can support pagination and sorting.\n\n"
|
@Operation(summary = "List Clients", description = "The list capability of clients can support pagination and sorting.\n\n"
|
||||||
+ "Example Requests:\n" + "\n" + "clients\n" + "\n" + "clients?fields=displayName,officeName,timeline\n" + "\n"
|
+ "Example Requests:\n" + "\n" + "clients\n" + "\n" + "clients?fields=displayName,officeName,timeline\n" + "\n"
|
||||||
+ "clients?offset=10&limit=50\n" + "\n" + "clients?orderBy=displayName&sortOrder=DESC")
|
+ "clients?offset=10&limit=50\n" + "\n" + "clients?orderBy=displayName&sortOrder=DESC\n\n"
|
||||||
|
+ "clients?fromDate=2026-01-01&toDate=2026-01-31 (submittedon_date range, yyyy-MM-dd, inclusive)")
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsResponse.class)))
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsResponse.class)))
|
||||||
public String retrieveAll(@Context final UriInfo uriInfo,
|
public String retrieveAll(@Context final UriInfo uriInfo,
|
||||||
@QueryParam("officeId") @Parameter(description = "officeId") final Long officeId,
|
@QueryParam("officeId") @Parameter(description = "officeId") final Long officeId,
|
||||||
@@ -157,10 +159,15 @@ public class ClientsApiResource {
|
|||||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
||||||
@QueryParam("orphansOnly") @Parameter(description = "orphansOnly") final Boolean orphansOnly,
|
@QueryParam("orphansOnly") @Parameter(description = "orphansOnly") final Boolean orphansOnly,
|
||||||
@QueryParam("legalForm") final Integer legalForm) {
|
@QueryParam("legalForm") final Integer legalForm,
|
||||||
|
@QueryParam("fromDate") @Parameter(description = "fromDate") final String fromDate,
|
||||||
|
@QueryParam("toDate") @Parameter(description = "toDate") final String toDate) {
|
||||||
|
|
||||||
|
final LocalDate fromDateParsed = StringUtils.isNotBlank(fromDate) ? LocalDate.parse(fromDate) : null;
|
||||||
|
final LocalDate toDateParsed = StringUtils.isNotBlank(toDate) ? LocalDate.parse(toDate) : null;
|
||||||
|
|
||||||
return retrieveAll(uriInfo, officeId, externalId, displayName, firstname, lastname, status, legalForm, hierarchy, offset, limit,
|
return retrieveAll(uriInfo, officeId, externalId, displayName, firstname, lastname, status, legalForm, hierarchy, offset, limit,
|
||||||
orderBy, sortOrder, orphansOnly, false);
|
orderBy, sortOrder, orphansOnly, false, fromDateParsed, toDateParsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -437,7 +444,7 @@ public class ClientsApiResource {
|
|||||||
public String retrieveAll(final UriInfo uriInfo, final Long officeId, final String externalId, final String displayName,
|
public String retrieveAll(final UriInfo uriInfo, final Long officeId, final String externalId, final String displayName,
|
||||||
final String firstname, final String lastname, final String status, final Integer legalForm, final String hierarchy,
|
final String firstname, final String lastname, final String status, final Integer legalForm, final String hierarchy,
|
||||||
final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
|
final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
|
||||||
final boolean isSelfUser) {
|
final boolean isSelfUser, final LocalDate fromDate, final LocalDate toDate) {
|
||||||
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
|
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
|
||||||
sqlValidator.validate(orderBy);
|
sqlValidator.validate(orderBy);
|
||||||
sqlValidator.validate(sortOrder);
|
sqlValidator.validate(sortOrder);
|
||||||
@@ -445,7 +452,8 @@ public class ClientsApiResource {
|
|||||||
sqlValidator.validate(hierarchy);
|
sqlValidator.validate(hierarchy);
|
||||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).officeId(officeId).externalId(externalId)
|
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).officeId(officeId).externalId(externalId)
|
||||||
.name(displayName).hierarchy(hierarchy).firstname(firstname).lastname(lastname).status(status).orphansOnly(orphansOnly)
|
.name(displayName).hierarchy(hierarchy).firstname(firstname).lastname(lastname).status(status).orphansOnly(orphansOnly)
|
||||||
.isSelfUser(isSelfUser).offset(offset).orderBy(orderBy).sortOrder(sortOrder).legalForm(legalForm).build();
|
.isSelfUser(isSelfUser).offset(offset).orderBy(orderBy).sortOrder(sortOrder).legalForm(legalForm).fromDate(fromDate)
|
||||||
|
.toDate(toDate).build();
|
||||||
final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
|
final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
|
||||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
|
return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
|
||||||
|
|||||||
+10
@@ -178,6 +178,16 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
|||||||
extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
|
extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (searchParameters.hasFromDate()) {
|
||||||
|
paramList.add(searchParameters.getFromDate().toString());
|
||||||
|
extraCriteria += " and c.submittedon_date >= ?::date ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchParameters.hasToDate()) {
|
||||||
|
paramList.add(searchParameters.getToDate().toString());
|
||||||
|
extraCriteria += " and c.submittedon_date <= ?::date ";
|
||||||
|
}
|
||||||
|
|
||||||
if (firstname != null) {
|
if (firstname != null) {
|
||||||
paramList.add(firstname);
|
paramList.add(firstname);
|
||||||
extraCriteria += " and c.firstname like ? ";
|
extraCriteria += " and c.firstname like ? ";
|
||||||
|
|||||||
+10
-5
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.sanctions.data;
|
package org.apache.fineract.portfolio.sanctions.data;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,10 +33,11 @@ public final class SanctionsScreeningResult {
|
|||||||
private final String message;
|
private final String message;
|
||||||
private final int matchCount;
|
private final int matchCount;
|
||||||
private final List<SanctionsMatchData> matches;
|
private final List<SanctionsMatchData> matches;
|
||||||
|
private final OffsetDateTime listDataAsOf;
|
||||||
|
|
||||||
private SanctionsScreeningResult(final Long screeningLogId, final String screenedName, final boolean matchFound,
|
private SanctionsScreeningResult(final Long screeningLogId, final String screenedName, final boolean matchFound,
|
||||||
final String status, final String message, final int matchCount,
|
final String status, final String message, final int matchCount,
|
||||||
final List<SanctionsMatchData> matches) {
|
final List<SanctionsMatchData> matches, final OffsetDateTime listDataAsOf) {
|
||||||
this.screeningLogId = screeningLogId;
|
this.screeningLogId = screeningLogId;
|
||||||
this.screenedName = screenedName;
|
this.screenedName = screenedName;
|
||||||
this.matchFound = matchFound;
|
this.matchFound = matchFound;
|
||||||
@@ -43,20 +45,22 @@ public final class SanctionsScreeningResult {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
this.matchCount = matchCount;
|
this.matchCount = matchCount;
|
||||||
this.matches = matches;
|
this.matches = matches;
|
||||||
|
this.listDataAsOf = listDataAsOf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SanctionsScreeningResult matched(final Long screeningLogId, final String screenedName,
|
public static SanctionsScreeningResult matched(final Long screeningLogId, final String screenedName,
|
||||||
final List<SanctionsMatchData> matches) {
|
final List<SanctionsMatchData> matches, final OffsetDateTime listDataAsOf) {
|
||||||
return new SanctionsScreeningResult(screeningLogId, screenedName, true, "FLAGGED",
|
return new SanctionsScreeningResult(screeningLogId, screenedName, true, "FLAGGED",
|
||||||
"Potential match found in UN Security Council consolidated sanctions list. "
|
"Potential match found in UN Security Council consolidated sanctions list. "
|
||||||
+ "Manual review required before proceeding.",
|
+ "Manual review required before proceeding.",
|
||||||
matches.size(), matches);
|
matches.size(), matches, listDataAsOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SanctionsScreeningResult clear(final Long screeningLogId, final String screenedName) {
|
public static SanctionsScreeningResult clear(final Long screeningLogId, final String screenedName,
|
||||||
|
final OffsetDateTime listDataAsOf) {
|
||||||
return new SanctionsScreeningResult(screeningLogId, screenedName, false, "CLEAR",
|
return new SanctionsScreeningResult(screeningLogId, screenedName, false, "CLEAR",
|
||||||
"No match found in UN Security Council consolidated sanctions list.",
|
"No match found in UN Security Council consolidated sanctions list.",
|
||||||
0, List.of());
|
0, List.of(), listDataAsOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getScreeningLogId() { return screeningLogId; }
|
public Long getScreeningLogId() { return screeningLogId; }
|
||||||
@@ -66,4 +70,5 @@ public final class SanctionsScreeningResult {
|
|||||||
public String getMessage() { return message; }
|
public String getMessage() { return message; }
|
||||||
public int getMatchCount() { return matchCount; }
|
public int getMatchCount() { return matchCount; }
|
||||||
public List<SanctionsMatchData> getMatches() { return matches; }
|
public List<SanctionsMatchData> getMatches() { return matches; }
|
||||||
|
public OffsetDateTime getListDataAsOf() { return listDataAsOf; }
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.sanctions.service;
|
package org.apache.fineract.portfolio.sanctions.service;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -54,18 +55,19 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
|||||||
final Long clientId, final String ipAddress) {
|
final Long clientId, final String ipAddress) {
|
||||||
if (name == null || name.isBlank()) {
|
if (name == null || name.isBlank()) {
|
||||||
final Long logId = saveLog(name, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
final Long logId = saveLog(name, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
||||||
return SanctionsScreeningResult.clear(logId, name);
|
return SanctionsScreeningResult.clear(logId, name, sanctionsEntryRepository.findListLastLoadedOn().orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
final String trimmedName = name.trim();
|
final String trimmedName = name.trim();
|
||||||
LOG.info("Screening name against sanctions list: {}", trimmedName);
|
LOG.info("Screening name against sanctions list: {}", trimmedName);
|
||||||
|
|
||||||
|
final OffsetDateTime listDataAsOf = sanctionsEntryRepository.findListLastLoadedOn().orElse(null);
|
||||||
final List<SanctionsEntry> matches = sanctionsEntryRepository.searchByNameOrAlias(trimmedName);
|
final List<SanctionsEntry> matches = sanctionsEntryRepository.searchByNameOrAlias(trimmedName);
|
||||||
|
|
||||||
if (matches.isEmpty()) {
|
if (matches.isEmpty()) {
|
||||||
LOG.info("No sanctions match found for: {}", trimmedName);
|
LOG.info("No sanctions match found for: {}", trimmedName);
|
||||||
final Long logId = saveLog(trimmedName, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
final Long logId = saveLog(trimmedName, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
||||||
return SanctionsScreeningResult.clear(logId, trimmedName);
|
return SanctionsScreeningResult.clear(logId, trimmedName, listDataAsOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.warn("Sanctions match found for '{}': {} potential match(es)", trimmedName, matches.size());
|
LOG.warn("Sanctions match found for '{}': {} potential match(es)", trimmedName, matches.size());
|
||||||
@@ -84,7 +86,7 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
|||||||
|
|
||||||
final Long logId = saveLog(trimmedName, "FLAGGED", matches.size(), matchedEntryIds, matchedNames, source, clientId, ipAddress);
|
final Long logId = saveLog(trimmedName, "FLAGGED", matches.size(), matchedEntryIds, matchedNames, source, clientId, ipAddress);
|
||||||
|
|
||||||
return SanctionsScreeningResult.matched(logId, trimmedName, matchDataList);
|
return SanctionsScreeningResult.matched(logId, trimmedName, matchDataList, listDataAsOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -135,7 +135,7 @@ public class SelfClientsApiResource {
|
|||||||
final String hierarchy = null;
|
final String hierarchy = null;
|
||||||
final Boolean orphansOnly = null;
|
final Boolean orphansOnly = null;
|
||||||
return this.clientApiResource.retrieveAll(uriInfo, officeId, externalId, displayName, firstname, lastname, status, legalForm,
|
return this.clientApiResource.retrieveAll(uriInfo, officeId, externalId, displayName, firstname, lastname, status, legalForm,
|
||||||
hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, true);
|
hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, true, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
|||||||
Reference in New Issue
Block a user