Compare commits
2
Commits
7c15fbb0c7
...
b1cfa996b4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1cfa996b4 | ||
|
|
02541eed6c |
+9
@@ -18,7 +18,9 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.sanctions.domain;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
@@ -40,4 +42,11 @@ public interface SanctionsEntryRepository extends JpaRepository<SanctionsEntry,
|
||||
void deleteAllByActiveTrue();
|
||||
|
||||
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.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.data.AuditData;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.AuditReadPlatformService;
|
||||
@@ -142,7 +143,8 @@ public class ClientsApiResource {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@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"
|
||||
+ "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)))
|
||||
public String retrieveAll(@Context final UriInfo uriInfo,
|
||||
@QueryParam("officeId") @Parameter(description = "officeId") final Long officeId,
|
||||
@@ -157,10 +159,15 @@ public class ClientsApiResource {
|
||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
||||
@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,
|
||||
orderBy, sortOrder, orphansOnly, false);
|
||||
orderBy, sortOrder, orphansOnly, false, fromDateParsed, toDateParsed);
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -437,7 +444,7 @@ public class ClientsApiResource {
|
||||
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 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);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
@@ -445,7 +452,8 @@ public class ClientsApiResource {
|
||||
sqlValidator.validate(hierarchy);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).officeId(officeId).externalId(externalId)
|
||||
.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 ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
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() + " ";
|
||||
}
|
||||
|
||||
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) {
|
||||
paramList.add(firstname);
|
||||
extraCriteria += " and c.firstname like ? ";
|
||||
|
||||
+10
-5
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.sanctions.data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -32,10 +33,11 @@ public final class SanctionsScreeningResult {
|
||||
private final String message;
|
||||
private final int matchCount;
|
||||
private final List<SanctionsMatchData> matches;
|
||||
private final OffsetDateTime listDataAsOf;
|
||||
|
||||
private SanctionsScreeningResult(final Long screeningLogId, final String screenedName, final boolean matchFound,
|
||||
final String status, final String message, final int matchCount,
|
||||
final List<SanctionsMatchData> matches) {
|
||||
final List<SanctionsMatchData> matches, final OffsetDateTime listDataAsOf) {
|
||||
this.screeningLogId = screeningLogId;
|
||||
this.screenedName = screenedName;
|
||||
this.matchFound = matchFound;
|
||||
@@ -43,20 +45,22 @@ public final class SanctionsScreeningResult {
|
||||
this.message = message;
|
||||
this.matchCount = matchCount;
|
||||
this.matches = matches;
|
||||
this.listDataAsOf = listDataAsOf;
|
||||
}
|
||||
|
||||
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",
|
||||
"Potential match found in UN Security Council consolidated sanctions list. "
|
||||
+ "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",
|
||||
"No match found in UN Security Council consolidated sanctions list.",
|
||||
0, List.of());
|
||||
0, List.of(), listDataAsOf);
|
||||
}
|
||||
|
||||
public Long getScreeningLogId() { return screeningLogId; }
|
||||
@@ -66,4 +70,5 @@ public final class SanctionsScreeningResult {
|
||||
public String getMessage() { return message; }
|
||||
public int getMatchCount() { return matchCount; }
|
||||
public List<SanctionsMatchData> getMatches() { return matches; }
|
||||
public OffsetDateTime getListDataAsOf() { return listDataAsOf; }
|
||||
}
|
||||
|
||||
+5
-3
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.sanctions.service;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -54,18 +55,19 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
||||
final Long clientId, final String ipAddress) {
|
||||
if (name == null || name.isBlank()) {
|
||||
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();
|
||||
LOG.info("Screening name against sanctions list: {}", trimmedName);
|
||||
|
||||
final OffsetDateTime listDataAsOf = sanctionsEntryRepository.findListLastLoadedOn().orElse(null);
|
||||
final List<SanctionsEntry> matches = sanctionsEntryRepository.searchByNameOrAlias(trimmedName);
|
||||
|
||||
if (matches.isEmpty()) {
|
||||
LOG.info("No sanctions match found for: {}", trimmedName);
|
||||
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());
|
||||
@@ -84,7 +86,7 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
||||
|
||||
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
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ public class SelfClientsApiResource {
|
||||
final String hierarchy = null;
|
||||
final Boolean orphansOnly = null;
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user