feat: add database export API, welcome kit return tracking, and sanctions/mobile lookup updates
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (progressive-loan, build-progressive-loan) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 8 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 9 of 10) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - PostgreSQL / test (test-core-5) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MariaDB / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-1) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-2) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-3) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-4) (push) Canceled after 0s
Fineract Cargo & Unit- & Integration tests - MySQL / test (test-core-5) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, build-core) (push) Canceled after 0s
Fineract Build & Cucumber tests (without E2E tests) / verify (main, cucumber) (push) Canceled after 0s
Fineract Docker Builds / build (false, docker-compose-postgresql.yml, postgresql) (push) Canceled after 0s
Fineract Docker Builds / build (true, docker-compose.yml, mariadb) (push) Canceled after 0s
Fineract Documentation build / build (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 1 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 10 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 2 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 3 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 4 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 5 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 6 of 10) (push) Canceled after 0s
Fineract E2E Tests / E2E Tests (Shard 7 of 10) (push) Canceled after 0s
Fineract Liquibase Only mode - PostgreSQL / test (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with ActiveMQ (push) Canceled after 0s
Fineract Messaging Smoke Tests / Smoke Test with Kafka (push) Canceled after 0s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,19 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
|
||||
private Boolean alternateDipVerified;
|
||||
private Long alternateDipLookupLogId;
|
||||
private String alternateDipTimestamp;
|
||||
/**
|
||||
* Live DIP/MNRL status of the number, resolved from the latest lookup rather than the flag
|
||||
* captured at onboarding. A customer cleared at onboarding whose number is later reported or
|
||||
* reassigned would otherwise keep showing "Verified" forever.
|
||||
*/
|
||||
private String dipLiveStatus;
|
||||
private String alternateDipLiveStatus;
|
||||
/**
|
||||
* Disposition of the client's most recent flagged sanctions alert, or null when they carry
|
||||
* none. Like the DIP status this is resolved live: nameVerified is captured at onboarding and
|
||||
* would keep reading "Clear" after the client was added to a later list snapshot.
|
||||
*/
|
||||
private String sanctionsAlertStatus;
|
||||
private Boolean nameVerified;
|
||||
private Long screeningLogId;
|
||||
private Boolean isStaff;
|
||||
@@ -436,6 +449,11 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
|
||||
data.alternateDipVerified = clientData.alternateDipVerified;
|
||||
data.alternateDipLookupLogId = clientData.alternateDipLookupLogId;
|
||||
data.alternateDipTimestamp = clientData.alternateDipTimestamp;
|
||||
// Carried across too: these helpers rebuild ClientData field by field, so a field missing
|
||||
// here is silently dropped from the payload the client view loads.
|
||||
data.dipLiveStatus = clientData.dipLiveStatus;
|
||||
data.alternateDipLiveStatus = clientData.alternateDipLiveStatus;
|
||||
data.sanctionsAlertStatus = clientData.sanctionsAlertStatus;
|
||||
data.nameVerified = clientData.nameVerified;
|
||||
data.screeningLogId = clientData.screeningLogId;
|
||||
return data;
|
||||
@@ -498,6 +516,11 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
|
||||
data.alternateDipVerified = clientData.alternateDipVerified;
|
||||
data.alternateDipLookupLogId = clientData.alternateDipLookupLogId;
|
||||
data.alternateDipTimestamp = clientData.alternateDipTimestamp;
|
||||
// Carried across too: these helpers rebuild ClientData field by field, so a field missing
|
||||
// here is silently dropped from the payload the client view loads.
|
||||
data.dipLiveStatus = clientData.dipLiveStatus;
|
||||
data.alternateDipLiveStatus = clientData.alternateDipLiveStatus;
|
||||
data.sanctionsAlertStatus = clientData.sanctionsAlertStatus;
|
||||
data.nameVerified = clientData.nameVerified;
|
||||
data.screeningLogId = clientData.screeningLogId;
|
||||
return data;
|
||||
@@ -550,6 +573,11 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
|
||||
data.alternateDipVerified = clientData.alternateDipVerified;
|
||||
data.alternateDipLookupLogId = clientData.alternateDipLookupLogId;
|
||||
data.alternateDipTimestamp = clientData.alternateDipTimestamp;
|
||||
// Carried across too: these helpers rebuild ClientData field by field, so a field missing
|
||||
// here is silently dropped from the payload the client view loads.
|
||||
data.dipLiveStatus = clientData.dipLiveStatus;
|
||||
data.alternateDipLiveStatus = clientData.alternateDipLiveStatus;
|
||||
data.sanctionsAlertStatus = clientData.sanctionsAlertStatus;
|
||||
data.nameVerified = clientData.nameVerified;
|
||||
data.screeningLogId = clientData.screeningLogId;
|
||||
return data;
|
||||
|
||||
+41
@@ -96,6 +96,24 @@ public class SanctionsScreeningLog extends AbstractAuditableWithUTCDateTimeCusto
|
||||
@Column(name = "frozen_on")
|
||||
private OffsetDateTime frozenOn;
|
||||
|
||||
// ── STR tracking ──────────────────────────────────────────────────────────
|
||||
// A confirmed match is reportable to FIU-IND. Tracked here rather than as an AML alert: an
|
||||
// AML alert is built around a monetary transaction, whereas this is triggered by who the
|
||||
// customer is, so the two are surfaced as separate streams on the FIU screen.
|
||||
|
||||
/** NULL until approval; then PENDING_FILING → FILED. */
|
||||
@Column(name = "str_status", length = 30)
|
||||
private String strStatus;
|
||||
|
||||
@Column(name = "str_due_date")
|
||||
private java.time.LocalDate strDueDate;
|
||||
|
||||
@Column(name = "str_raised_on")
|
||||
private OffsetDateTime strRaisedOn;
|
||||
|
||||
public static final String STR_PENDING_FILING = "PENDING_FILING";
|
||||
public static final String STR_FILED = "FILED";
|
||||
|
||||
public static final String RESULT_FLAGGED = "FLAGGED";
|
||||
public static final String RESULT_CLEAR = "CLEAR";
|
||||
public static final String DISPOSITION_PENDING = "PENDING_REVIEW";
|
||||
@@ -146,6 +164,11 @@ public class SanctionsScreeningLog extends AbstractAuditableWithUTCDateTimeCusto
|
||||
public void recordReporting(final String strReference, final OffsetDateTime nodalOfficerInformedOn) {
|
||||
if (strReference != null) {
|
||||
this.fiuStrReference = strReference;
|
||||
// Recording the FIU-IND acknowledgement is what closes the filing obligation, so the
|
||||
// case stops appearing as outstanding on the FIU screen.
|
||||
if (!strReference.isBlank()) {
|
||||
this.strStatus = STR_FILED;
|
||||
}
|
||||
}
|
||||
if (nodalOfficerInformedOn != null) {
|
||||
this.nodalOfficerInformedOn = nodalOfficerInformedOn;
|
||||
@@ -157,6 +180,21 @@ public class SanctionsScreeningLog extends AbstractAuditableWithUTCDateTimeCusto
|
||||
this.frozenOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the STR obligation on approval of a confirmed match. PMLA allows seven working days
|
||||
* from forming the suspicion; seven calendar days is the stricter reading.
|
||||
*/
|
||||
public void raiseStr() {
|
||||
if (this.strStatus != null) {
|
||||
return;
|
||||
}
|
||||
this.strStatus = STR_PENDING_FILING;
|
||||
this.strRaisedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
this.strDueDate = DateUtils.getBusinessLocalDate().plusDays(7);
|
||||
}
|
||||
|
||||
public boolean isStrPending() { return STR_PENDING_FILING.equals(this.strStatus); }
|
||||
|
||||
public boolean isFlagged() { return RESULT_FLAGGED.equals(this.screeningResult); }
|
||||
|
||||
public boolean isApprovedFalsePositive() {
|
||||
@@ -187,4 +225,7 @@ public class SanctionsScreeningLog extends AbstractAuditableWithUTCDateTimeCusto
|
||||
public OffsetDateTime getNodalOfficerInformedOn() { return nodalOfficerInformedOn; }
|
||||
public String getFrozenAccountIds() { return frozenAccountIds; }
|
||||
public OffsetDateTime getFrozenOn() { return frozenOn; }
|
||||
public String getStrStatus() { return strStatus; }
|
||||
public java.time.LocalDate getStrDueDate() { return strDueDate; }
|
||||
public OffsetDateTime getStrRaisedOn() { return strRaisedOn; }
|
||||
}
|
||||
|
||||
+8
@@ -59,4 +59,12 @@ public interface SanctionsScreeningLogRepository extends JpaRepository<Sanctions
|
||||
@Query("SELECT l FROM SanctionsScreeningLog l WHERE l.clientId = :clientId AND l.screeningResult = 'FLAGGED' "
|
||||
+ "ORDER BY l.screenedOn DESC")
|
||||
List<SanctionsScreeningLog> findFlaggedByClientId(@Param("clientId") Long clientId);
|
||||
|
||||
/**
|
||||
* Confirmed matches carrying an STR obligation — the sanctions stream on the FIU-IND screen.
|
||||
* Pass null for every STR regardless of whether it has been filed.
|
||||
*/
|
||||
@Query("SELECT l FROM SanctionsScreeningLog l WHERE l.strStatus IS NOT NULL "
|
||||
+ "AND (:status IS NULL OR l.strStatus = :status) ORDER BY l.strDueDate ASC, l.strRaisedOn DESC")
|
||||
List<SanctionsScreeningLog> findStrCases(@Param("status") String status);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user