Add all CBS custom modules: KYC, Sanctions, RBI Feed, OpenProject, Mobile Lookup, Charges, Avro schemas
Includes all prior work not yet committed on this branch: - KYC module (LincPay eKYC: PAN, Aadhaar OTP, GSTIN verification) - Sanctions screening (UN Security Council consolidated list) - RBI Feed integration with scheduled jobs - OpenProject API integration - Mobile lookup service - Charge module enhancements (current account support) - Client family member nominee fields - Avro schema updates - Liquibase migrations 0216-0224 - Postman collections for OpenProject, RBI Feeds, Sanctions - SQL seed scripts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
78f37257a7
commit
ba5365d4af
+1
@@ -60,6 +60,7 @@ public enum JobName {
|
||||
ACCRUAL_ACTIVITY_POSTING("Accrual Activity Posting"), //
|
||||
ADD_PERIODIC_ACCRUAL_ENTRIES_FOR_SAVINGS_WITH_INCOME_POSTED_AS_TRANSACTIONS("Add Accrual Transactions For Savings"), //
|
||||
JOURNAL_ENTRY_AGGREGATION("Journal Entry Aggregation"), //
|
||||
FETCH_RBI_RSS_FEEDS("Fetch RBI RSS Feeds"), //
|
||||
; //
|
||||
|
||||
private final String name;
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.infrastructure.openproject.data;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public final class OpenProjectProjectData {
|
||||
|
||||
private Long id;
|
||||
private String identifier;
|
||||
private String name;
|
||||
private boolean active;
|
||||
private String description;
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.infrastructure.openproject.data;
|
||||
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public final class OpenProjectStatusSummaryData {
|
||||
|
||||
private int totalWorkPackages;
|
||||
private Map<String, Integer> byStatus;
|
||||
private Map<String, Integer> byType;
|
||||
private Map<String, Integer> byPriority;
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.infrastructure.openproject.data;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public final class OpenProjectWorkPackageData {
|
||||
|
||||
private Long id;
|
||||
private String subject;
|
||||
private String description;
|
||||
private String typeName;
|
||||
private String statusName;
|
||||
private String priorityName;
|
||||
private String assigneeName;
|
||||
private Integer percentageDone;
|
||||
private String startDate;
|
||||
private String dueDate;
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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.infrastructure.rbifeed.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public final class RbiFeedItemData implements Serializable {
|
||||
|
||||
private Long id;
|
||||
private String title;
|
||||
private String description;
|
||||
private String link;
|
||||
private LocalDateTime pubDate;
|
||||
private String category;
|
||||
private LocalDateTime fetchedOn;
|
||||
private Long openProjectWpId;
|
||||
private String openProjectWpStatus;
|
||||
private Integer openProjectWpProgress;
|
||||
|
||||
public static RbiFeedItemData instance(final Long id, final String title, final String description, final String link,
|
||||
final LocalDateTime pubDate, final String category, final LocalDateTime fetchedOn,
|
||||
final Long openProjectWpId, final String openProjectWpStatus, final Integer openProjectWpProgress) {
|
||||
return new RbiFeedItemData().setId(id).setTitle(title).setDescription(description).setLink(link).setPubDate(pubDate)
|
||||
.setCategory(category).setFetchedOn(fetchedOn)
|
||||
.setOpenProjectWpId(openProjectWpId).setOpenProjectWpStatus(openProjectWpStatus)
|
||||
.setOpenProjectWpProgress(openProjectWpProgress);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.infrastructure.rbifeed.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_rbi_feed_item", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "link" }, name = "uk_rbi_feed_item_link") })
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class RbiFeedItem extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@Column(name = "title", nullable = false, length = 1000)
|
||||
private String title;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(name = "link", nullable = false, length = 1000)
|
||||
private String link;
|
||||
|
||||
@Column(name = "pub_date")
|
||||
private LocalDateTime pubDate;
|
||||
|
||||
@Column(name = "category", nullable = false, length = 50)
|
||||
private String category;
|
||||
|
||||
@Column(name = "fetched_on")
|
||||
private LocalDateTime fetchedOn;
|
||||
|
||||
@Column(name = "openproject_wp_id")
|
||||
private Long openProjectWpId;
|
||||
|
||||
@Column(name = "openproject_wp_status", length = 100)
|
||||
private String openProjectWpStatus;
|
||||
|
||||
@Column(name = "openproject_wp_progress")
|
||||
private Integer openProjectWpProgress;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.infrastructure.rbifeed.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface RbiFeedItemRepository extends JpaRepository<RbiFeedItem, Long>, JpaSpecificationExecutor<RbiFeedItem> {
|
||||
|
||||
boolean existsByLink(String link);
|
||||
|
||||
List<RbiFeedItem> findByCategoryOrderByPubDateDesc(String category);
|
||||
|
||||
List<RbiFeedItem> findAllByOrderByPubDateDesc();
|
||||
|
||||
List<RbiFeedItem> findByCategoryAndOpenProjectWpIdIsNotNull(String category);
|
||||
|
||||
List<RbiFeedItem> findByCategoryAndOpenProjectWpIdIsNull(String category);
|
||||
}
|
||||
@@ -87,6 +87,8 @@ public final class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
private final List<EnumOptionData> clientChargeTimeTypeOptions;
|
||||
private final List<EnumOptionData> shareChargeCalculationTypeOptions;
|
||||
private final List<EnumOptionData> shareChargeTimeTypeOptions;
|
||||
private final List<EnumOptionData> currentAccountChargeCalculationTypeOptions;
|
||||
private final List<EnumOptionData> currentAccountChargeTimeTypeOptions;
|
||||
|
||||
private final List<EnumOptionData> feeFrequencyOptions;
|
||||
|
||||
@@ -113,6 +115,8 @@ public final class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
.taxGroupOptions(template.getTaxGroupOptions())
|
||||
.shareChargeCalculationTypeOptions(template.getShareChargeCalculationTypeOptions())
|
||||
.shareChargeTimeTypeOptions(template.getShareChargeTimeTypeOptions())
|
||||
.currentAccountChargeCalculationTypeOptions(template.getCurrentAccountChargeCalculationTypeOptions())
|
||||
.currentAccountChargeTimeTypeOptions(template.getCurrentAccountChargeTimeTypeOptions())
|
||||
.accountMappingForChargeConfig(template.getAccountMappingForChargeConfig())
|
||||
.expenseAccountOptions(template.getExpenseAccountOptions()).assetAccountOptions(template.getAssetAccountOptions()).build();
|
||||
}
|
||||
|
||||
+7
@@ -72,6 +72,13 @@ public enum ChargeTimeType {
|
||||
ChargeTimeType.SAVINGS_NOACTIVITY_FEE.getValue() };
|
||||
}
|
||||
|
||||
public static Object[] validCurrentAccountValues() {
|
||||
return new Integer[] { ChargeTimeType.SPECIFIED_DUE_DATE.getValue(), ChargeTimeType.SAVINGS_ACTIVATION.getValue(),
|
||||
ChargeTimeType.SAVINGS_CLOSURE.getValue(), ChargeTimeType.WITHDRAWAL_FEE.getValue(), ChargeTimeType.ANNUAL_FEE.getValue(),
|
||||
ChargeTimeType.MONTHLY_FEE.getValue(), ChargeTimeType.OVERDRAFT_FEE.getValue(), ChargeTimeType.WEEKLY_FEE.getValue(),
|
||||
ChargeTimeType.SAVINGS_NOACTIVITY_FEE.getValue() };
|
||||
}
|
||||
|
||||
public static Object[] validClientValues() {
|
||||
return new Integer[] { ChargeTimeType.SPECIFIED_DUE_DATE.getValue() };
|
||||
}
|
||||
|
||||
+1
@@ -181,6 +181,7 @@ public class ClientApiConstants {
|
||||
|
||||
public static final String datatables = "datatables";
|
||||
public static final String obligeeData = "ObligeeDetails";
|
||||
public static final String kycParamName = "kyc";
|
||||
|
||||
public static final String clientEntityName = "clients";
|
||||
|
||||
|
||||
+4
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.client.data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -53,4 +54,7 @@ public class ClientFamilyMemberRequest implements Serializable {
|
||||
private Long id;
|
||||
private Long age;
|
||||
private Long professionId;
|
||||
private String nominationType;
|
||||
private BigDecimal percentageShare;
|
||||
private String remarks;
|
||||
}
|
||||
|
||||
+7
@@ -20,6 +20,7 @@
|
||||
package org.apache.fineract.portfolio.client.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import lombok.Builder;
|
||||
@@ -66,6 +67,12 @@ public final class ClientFamilyMembersData implements Serializable {
|
||||
|
||||
private final Boolean isDependent;
|
||||
|
||||
private final String nominationType;
|
||||
|
||||
private final BigDecimal percentageShare;
|
||||
|
||||
private final String remarks;
|
||||
|
||||
// template holder
|
||||
private final Collection<CodeValueData> relationshipIdOptions;
|
||||
private final Collection<CodeValueData> genderIdOptions;
|
||||
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
/**
|
||||
* 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.client.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import java.time.LocalDate;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Represents a client's KYC (Know Your Customer) record as mandated by the Reserve Bank of India
|
||||
* Master Direction on KYC (updated 2023).
|
||||
*
|
||||
* <p>One record per client. Contains:
|
||||
* <ul>
|
||||
* <li>PAN verification (Permanent Account Number – Income Tax Dept)</li>
|
||||
* <li>Aadhaar verification (12-digit UIDAI number, stored masked)</li>
|
||||
* <li>GSTIN verification (for non-individual/business clients)</li>
|
||||
* <li>Risk category, CKYCRR number, expiry & lifecycle timestamps</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_client_kyc", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "client_id" }, name = "uq_m_client_kyc_client_id") })
|
||||
public class ClientKyc extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "client_id", nullable = false)
|
||||
private Client client;
|
||||
|
||||
/** 100 = PENDING, 200 = IN_PROGRESS, 300 = VERIFIED, 400 = REJECTED, 500 = EXPIRED */
|
||||
@Column(name = "kyc_status", nullable = false)
|
||||
private Integer kycStatus;
|
||||
|
||||
/** 1 = INDIVIDUAL, 2 = NON_INDIVIDUAL */
|
||||
@Column(name = "kyc_type", nullable = false)
|
||||
private Integer kycType;
|
||||
|
||||
/** 1 = LOW, 2 = MEDIUM, 3 = HIGH */
|
||||
@Column(name = "risk_category", nullable = false)
|
||||
private Integer riskCategory;
|
||||
|
||||
// ── PAN (Permanent Account Number) ──────────────────────────────────────
|
||||
@Column(name = "pan_number", length = 10)
|
||||
private String panNumber;
|
||||
|
||||
@Column(name = "pan_verified", nullable = false)
|
||||
private boolean panVerified;
|
||||
|
||||
@Column(name = "pan_verified_on")
|
||||
private LocalDate panVerifiedOn;
|
||||
|
||||
@Column(name = "pan_full_name", length = 200)
|
||||
private String panFullName;
|
||||
|
||||
/** Y = YES, N = NO, NA = Not Available (Aadhaar seeding status on PAN card) */
|
||||
@Column(name = "pan_aadhaar_seeding_status", length = 5)
|
||||
private String panAadhaarSeedingStatus;
|
||||
|
||||
/** PAN card category: individual, company, firm, etc. */
|
||||
@Column(name = "pan_category", length = 50)
|
||||
private String panCategory;
|
||||
|
||||
/** PAN validation status: valid or invalid */
|
||||
@Column(name = "pan_status", length = 20)
|
||||
private String panStatus;
|
||||
|
||||
// ── Aadhaar ──────────────────────────────────────────────────────────────
|
||||
/** Stored masked (last 4 digits visible). Full number must never be persisted as per UIDAI directive. */
|
||||
@Column(name = "aadhaar_number_masked", length = 12)
|
||||
private String aadhaarNumberMasked;
|
||||
|
||||
@Column(name = "aadhaar_verified", nullable = false)
|
||||
private boolean aadhaarVerified;
|
||||
|
||||
@Column(name = "aadhaar_verified_on")
|
||||
private LocalDate aadhaarVerifiedOn;
|
||||
|
||||
// ── GSTIN (mandatory for NON_INDIVIDUAL clients) ─────────────────────────
|
||||
@Column(name = "gstin_number", length = 15)
|
||||
private String gstinNumber;
|
||||
|
||||
@Column(name = "gstin_verified", nullable = false)
|
||||
private boolean gstinVerified;
|
||||
|
||||
@Column(name = "gstin_verified_on")
|
||||
private LocalDate gstinVerifiedOn;
|
||||
|
||||
// ── CKYCRR ───────────────────────────────────────────────────────────────
|
||||
/** Central KYC Records Registry number issued by CERSAI */
|
||||
@Column(name = "ckyc_number", length = 14)
|
||||
private String ckycNumber;
|
||||
|
||||
// ── Lifecycle dates ───────────────────────────────────────────────────────
|
||||
@Column(name = "initiated_on")
|
||||
private LocalDate initiatedOn;
|
||||
|
||||
@Column(name = "verified_on")
|
||||
private LocalDate verifiedOn;
|
||||
|
||||
@Column(name = "rejected_on")
|
||||
private LocalDate rejectedOn;
|
||||
|
||||
/** KYC expiry date calculated based on risk category and verification date */
|
||||
@Column(name = "expiry_date")
|
||||
private LocalDate expiryDate;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
protected ClientKyc() {}
|
||||
|
||||
private ClientKyc(final Client client, final Integer kycType, final Integer riskCategory,
|
||||
final String panNumber, final String aadhaarNumberMasked, final String gstinNumber,
|
||||
final String remarks) {
|
||||
this.client = client;
|
||||
this.kycStatus = ClientKycStatus.PENDING.getValue();
|
||||
this.kycType = kycType;
|
||||
this.riskCategory = riskCategory;
|
||||
this.panNumber = StringUtils.defaultIfEmpty(panNumber, null);
|
||||
this.aadhaarNumberMasked = StringUtils.defaultIfEmpty(aadhaarNumberMasked, null);
|
||||
this.gstinNumber = StringUtils.defaultIfEmpty(gstinNumber, null);
|
||||
this.remarks = StringUtils.defaultIfEmpty(remarks, null);
|
||||
this.panVerified = false;
|
||||
this.aadhaarVerified = false;
|
||||
this.gstinVerified = false;
|
||||
this.initiatedOn = LocalDate.now();
|
||||
}
|
||||
|
||||
public static ClientKyc initiateKyc(final Client client, final JsonCommand command) {
|
||||
final Integer kycType = command.integerValueOfParameterNamed("kycType");
|
||||
final Integer riskCategory = command.integerValueOfParameterNamed("riskCategory");
|
||||
final String panNumber = command.stringValueOfParameterNamed("panNumber");
|
||||
final String aadhaarNumberMasked = command.stringValueOfParameterNamed("aadhaarNumberMasked");
|
||||
final String gstinNumber = command.stringValueOfParameterNamed("gstinNumber");
|
||||
final String remarks = command.stringValueOfParameterNamed("remarks");
|
||||
return new ClientKyc(client, kycType, riskCategory, panNumber, aadhaarNumberMasked, gstinNumber, remarks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used when KYC is initiated inline during client registration.
|
||||
* Accepts individual field values directly (avoids needing a JsonCommand sub-object).
|
||||
*/
|
||||
public static ClientKyc initiateKyc(final Client client, final Integer kycType, final Integer riskCategory,
|
||||
final String panNumber, final String aadhaarNumberMasked, final String gstinNumber, final String remarks) {
|
||||
return new ClientKyc(client, kycType, riskCategory, panNumber, aadhaarNumberMasked, gstinNumber, remarks);
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
||||
|
||||
final String panNumberParam = "panNumber";
|
||||
if (command.isChangeInStringParameterNamed(panNumberParam, this.panNumber)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(panNumberParam);
|
||||
actualChanges.put(panNumberParam, newValue);
|
||||
this.panNumber = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
final String aadhaarParam = "aadhaarNumberMasked";
|
||||
if (command.isChangeInStringParameterNamed(aadhaarParam, this.aadhaarNumberMasked)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(aadhaarParam);
|
||||
actualChanges.put(aadhaarParam, newValue);
|
||||
this.aadhaarNumberMasked = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
final String gstinParam = "gstinNumber";
|
||||
if (command.isChangeInStringParameterNamed(gstinParam, this.gstinNumber)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(gstinParam);
|
||||
actualChanges.put(gstinParam, newValue);
|
||||
this.gstinNumber = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
final String riskParam = "riskCategory";
|
||||
if (command.isChangeInIntegerParameterNamed(riskParam, this.riskCategory)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(riskParam);
|
||||
actualChanges.put(riskParam, newValue);
|
||||
this.riskCategory = newValue;
|
||||
}
|
||||
|
||||
final String remarksParam = "remarks";
|
||||
if (command.isChangeInStringParameterNamed(remarksParam, this.remarks)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(remarksParam);
|
||||
actualChanges.put(remarksParam, newValue);
|
||||
this.remarks = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
/** Called when documents are submitted and 3rd-party verification is initiated. */
|
||||
public void markInProgress() {
|
||||
this.kycStatus = ClientKycStatus.IN_PROGRESS.getValue();
|
||||
}
|
||||
|
||||
/** Called after successful 3rd-party verification of all required documents. */
|
||||
public void markVerified(final String ckycNumber) {
|
||||
this.kycStatus = ClientKycStatus.VERIFIED.getValue();
|
||||
this.verifiedOn = LocalDate.now();
|
||||
this.ckycNumber = StringUtils.defaultIfEmpty(ckycNumber, null);
|
||||
|
||||
final KycRiskCategory risk = KycRiskCategory.fromInt(this.riskCategory);
|
||||
if (risk.getReKycYears() > 0) {
|
||||
this.expiryDate = this.verifiedOn.plusYears(risk.getReKycYears());
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when verification fails. */
|
||||
public void markRejected(final String remarks) {
|
||||
this.kycStatus = ClientKycStatus.REJECTED.getValue();
|
||||
this.rejectedOn = LocalDate.now();
|
||||
this.remarks = StringUtils.defaultIfEmpty(remarks, this.remarks);
|
||||
}
|
||||
|
||||
public void markPanVerified(final String fullName, final String aadhaarSeedingStatus,
|
||||
final String category, final String status) {
|
||||
this.panVerified = true;
|
||||
this.panVerifiedOn = LocalDate.now();
|
||||
this.panFullName = StringUtils.defaultIfEmpty(fullName, null);
|
||||
this.panAadhaarSeedingStatus = normalizeAadhaarSeedingStatus(aadhaarSeedingStatus);
|
||||
this.panCategory = StringUtils.defaultIfEmpty(category, null);
|
||||
this.panStatus = StringUtils.defaultIfEmpty(status, null);
|
||||
}
|
||||
|
||||
/** Normalize aadhaar seeding status: y→Y, n→N, anything else→NA */
|
||||
private String normalizeAadhaarSeedingStatus(final String raw) {
|
||||
if (raw == null || raw.isBlank()) {
|
||||
return "NA";
|
||||
}
|
||||
final String trimmed = raw.trim().toUpperCase();
|
||||
if ("Y".equals(trimmed) || "YES".equals(trimmed)) {
|
||||
return "Y";
|
||||
}
|
||||
if ("N".equals(trimmed) || "NO".equals(trimmed)) {
|
||||
return "N";
|
||||
}
|
||||
return "NA";
|
||||
}
|
||||
|
||||
public void markAadhaarVerified() {
|
||||
this.aadhaarVerified = true;
|
||||
this.aadhaarVerifiedOn = LocalDate.now();
|
||||
}
|
||||
|
||||
public void markGstinVerified() {
|
||||
this.gstinVerified = true;
|
||||
this.gstinVerifiedOn = LocalDate.now();
|
||||
}
|
||||
|
||||
// ── Accessors ─────────────────────────────────────────────────────────────
|
||||
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public ClientKycStatus getKycStatus() {
|
||||
return ClientKycStatus.fromInt(this.kycStatus);
|
||||
}
|
||||
|
||||
public Integer getKycStatusValue() {
|
||||
return kycStatus;
|
||||
}
|
||||
|
||||
public ClientKycType getKycType() {
|
||||
return ClientKycType.fromInt(this.kycType);
|
||||
}
|
||||
|
||||
public Integer getKycTypeValue() {
|
||||
return kycType;
|
||||
}
|
||||
|
||||
public KycRiskCategory getRiskCategory() {
|
||||
return KycRiskCategory.fromInt(this.riskCategory);
|
||||
}
|
||||
|
||||
public Integer getRiskCategoryValue() {
|
||||
return riskCategory;
|
||||
}
|
||||
|
||||
public String getPanNumber() {
|
||||
return panNumber;
|
||||
}
|
||||
|
||||
public boolean isPanVerified() {
|
||||
return panVerified;
|
||||
}
|
||||
|
||||
public LocalDate getPanVerifiedOn() {
|
||||
return panVerifiedOn;
|
||||
}
|
||||
|
||||
public String getPanFullName() {
|
||||
return panFullName;
|
||||
}
|
||||
|
||||
public String getPanAadhaarSeedingStatus() {
|
||||
return panAadhaarSeedingStatus;
|
||||
}
|
||||
|
||||
public String getPanCategory() {
|
||||
return panCategory;
|
||||
}
|
||||
|
||||
public String getPanStatus() {
|
||||
return panStatus;
|
||||
}
|
||||
|
||||
public String getAadhaarNumberMasked() {
|
||||
return aadhaarNumberMasked;
|
||||
}
|
||||
|
||||
public boolean isAadhaarVerified() {
|
||||
return aadhaarVerified;
|
||||
}
|
||||
|
||||
public LocalDate getAadhaarVerifiedOn() {
|
||||
return aadhaarVerifiedOn;
|
||||
}
|
||||
|
||||
public String getGstinNumber() {
|
||||
return gstinNumber;
|
||||
}
|
||||
|
||||
public boolean isGstinVerified() {
|
||||
return gstinVerified;
|
||||
}
|
||||
|
||||
public LocalDate getGstinVerifiedOn() {
|
||||
return gstinVerifiedOn;
|
||||
}
|
||||
|
||||
public String getCkycNumber() {
|
||||
return ckycNumber;
|
||||
}
|
||||
|
||||
public LocalDate getInitiatedOn() {
|
||||
return initiatedOn;
|
||||
}
|
||||
|
||||
public LocalDate getVerifiedOn() {
|
||||
return verifiedOn;
|
||||
}
|
||||
|
||||
public LocalDate getRejectedOn() {
|
||||
return rejectedOn;
|
||||
}
|
||||
|
||||
public LocalDate getExpiryDate() {
|
||||
return expiryDate;
|
||||
}
|
||||
|
||||
public String getRemarks() {
|
||||
return remarks;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.client.domain;
|
||||
|
||||
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;
|
||||
|
||||
public interface ClientKycRepository extends JpaRepository<ClientKyc, Long> {
|
||||
|
||||
Optional<ClientKyc> findByClientId(Long clientId);
|
||||
|
||||
@Query("SELECT k FROM ClientKyc k WHERE k.client.id = :clientId")
|
||||
Optional<ClientKyc> findKycByClientId(@Param("clientId") Long clientId);
|
||||
|
||||
boolean existsByClientId(Long clientId);
|
||||
|
||||
@Query("SELECT k FROM ClientKyc k WHERE k.kycStatus = :status")
|
||||
List<ClientKyc> findAllByStatus(@Param("status") Integer status);
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.client.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Wraps {@link ClientKycRepository} to surface consistent not-found exceptions.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ClientKycRepositoryWrapper {
|
||||
|
||||
private final ClientKycRepository repository;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ClientKyc findOneWithNotFoundDetection(final Long id) {
|
||||
return repository.findById(id).orElseThrow(
|
||||
() -> new org.apache.fineract.portfolio.client.exception.ClientKycNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ClientKyc findByClientIdWithNotFoundDetection(final Long clientId) {
|
||||
return repository.findByClientId(clientId).orElseThrow(
|
||||
() -> new org.apache.fineract.portfolio.client.exception.ClientKycNotFoundException(clientId));
|
||||
}
|
||||
|
||||
public ClientKyc saveAndFlush(final ClientKyc kyc) {
|
||||
return repository.saveAndFlush(kyc);
|
||||
}
|
||||
|
||||
public void delete(final ClientKyc kyc) {
|
||||
repository.delete(kyc);
|
||||
}
|
||||
|
||||
public boolean existsByClientId(final Long clientId) {
|
||||
return repository.existsByClientId(clientId);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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.client.domain;
|
||||
|
||||
/**
|
||||
* KYC status lifecycle as per RBI Master Direction on KYC (updated 2023).
|
||||
*
|
||||
* <pre>
|
||||
* PENDING -> IN_PROGRESS (documents submitted)
|
||||
* IN_PROGRESS -> VERIFIED (3rd-party check passed)
|
||||
* IN_PROGRESS -> REJECTED (3rd-party check failed / fraud detected)
|
||||
* VERIFIED -> EXPIRED (re-KYC due date crossed)
|
||||
* EXPIRED -> IN_PROGRESS (re-KYC initiated)
|
||||
* </pre>
|
||||
*/
|
||||
public enum ClientKycStatus {
|
||||
|
||||
INVALID(0, "clientKycStatus.invalid"),
|
||||
PENDING(100, "clientKycStatus.pending"),
|
||||
IN_PROGRESS(200, "clientKycStatus.inProgress"),
|
||||
VERIFIED(300, "clientKycStatus.verified"),
|
||||
REJECTED(400, "clientKycStatus.rejected"),
|
||||
EXPIRED(500, "clientKycStatus.expired");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
ClientKycStatus(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return this == PENDING;
|
||||
}
|
||||
|
||||
public boolean isInProgress() {
|
||||
return this == IN_PROGRESS;
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return this == VERIFIED;
|
||||
}
|
||||
|
||||
public boolean isRejected() {
|
||||
return this == REJECTED;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return this == EXPIRED;
|
||||
}
|
||||
|
||||
public static ClientKycStatus fromInt(final Integer value) {
|
||||
if (value == null) {
|
||||
return INVALID;
|
||||
}
|
||||
for (ClientKycStatus status : values()) {
|
||||
if (status.getValue().equals(value)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return INVALID;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.client.domain;
|
||||
|
||||
/**
|
||||
* KYC entity type as defined under RBI Master Direction on KYC.
|
||||
* INDIVIDUAL – natural persons.
|
||||
* NON_INDIVIDUAL – companies, trusts, partnerships, HUFs, etc.
|
||||
*/
|
||||
public enum ClientKycType {
|
||||
|
||||
INVALID(0, "clientKycType.invalid"),
|
||||
INDIVIDUAL(1, "clientKycType.individual"),
|
||||
NON_INDIVIDUAL(2, "clientKycType.nonIndividual");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
ClientKycType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public boolean isIndividual() {
|
||||
return this == INDIVIDUAL;
|
||||
}
|
||||
|
||||
public boolean isNonIndividual() {
|
||||
return this == NON_INDIVIDUAL;
|
||||
}
|
||||
|
||||
public static ClientKycType fromInt(final Integer value) {
|
||||
if (value == null) {
|
||||
return INVALID;
|
||||
}
|
||||
for (ClientKycType type : values()) {
|
||||
if (type.getValue().equals(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return INVALID;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.client.domain;
|
||||
|
||||
/**
|
||||
* Customer risk category used for periodic re-KYC frequency as per RBI guidelines.
|
||||
*
|
||||
* <ul>
|
||||
* <li>LOW – re-KYC every 10 years</li>
|
||||
* <li>MEDIUM – re-KYC every 8 years</li>
|
||||
* <li>HIGH – re-KYC every 2 years</li>
|
||||
* </ul>
|
||||
*/
|
||||
public enum KycRiskCategory {
|
||||
|
||||
INVALID(0, "kycRiskCategory.invalid", 0),
|
||||
LOW(1, "kycRiskCategory.low", 10),
|
||||
MEDIUM(2, "kycRiskCategory.medium", 8),
|
||||
HIGH(3, "kycRiskCategory.high", 2);
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
/** Re-KYC validity in years */
|
||||
private final int reKycYears;
|
||||
|
||||
KycRiskCategory(final Integer value, final String code, final int reKycYears) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
this.reKycYears = reKycYears;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public int getReKycYears() {
|
||||
return reKycYears;
|
||||
}
|
||||
|
||||
public static KycRiskCategory fromInt(final Integer value) {
|
||||
if (value == null) {
|
||||
return INVALID;
|
||||
}
|
||||
for (KycRiskCategory cat : values()) {
|
||||
if (cat.getValue().equals(value)) {
|
||||
return cat;
|
||||
}
|
||||
}
|
||||
return INVALID;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.client.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ClientKycAlreadyExistsException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ClientKycAlreadyExistsException(final Long clientId) {
|
||||
super("error.msg.client.kyc.already.exists",
|
||||
"A KYC record already exists for client with identifier " + clientId
|
||||
+ ". Use the update endpoint to modify it.",
|
||||
clientId);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.client.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ClientKycInvalidStateTransitionException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ClientKycInvalidStateTransitionException(final String action, final String currentState) {
|
||||
super("error.msg.client.kyc.invalid.state.transition",
|
||||
"KYC action [" + action + "] is not permitted when the KYC status is [" + currentState + "]",
|
||||
action, currentState);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.client.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class ClientKycNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public ClientKycNotFoundException(final Long id) {
|
||||
super("error.msg.client.kyc.not.found", "Client KYC record with identifier " + id + " does not exist", id);
|
||||
}
|
||||
|
||||
public ClientKycNotFoundException(final Long clientId, final boolean byClient) {
|
||||
super("error.msg.client.kyc.not.found",
|
||||
"No KYC record found for client with identifier " + clientId, clientId);
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.mobilelookup.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Activity log for each DIP / MNRL mobile number lookup request.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_mobile_lookup_log")
|
||||
@Getter
|
||||
public class MobileLookupLog extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@Column(name = "mobile_number", nullable = false, length = 15)
|
||||
private String mobileNumber;
|
||||
|
||||
/** CLEAN, SUSPECTED, DISCONNECTED, SUSPECTED_AND_DISCONNECTED, ERROR */
|
||||
@Column(name = "lookup_status", nullable = false, length = 50)
|
||||
private String lookupStatus;
|
||||
|
||||
@Column(name = "suspected", nullable = false)
|
||||
private boolean suspected;
|
||||
|
||||
@Column(name = "disconnected", nullable = false)
|
||||
private boolean disconnected;
|
||||
|
||||
@Column(name = "response_message", length = 500)
|
||||
private String responseMessage;
|
||||
|
||||
/** Full JSON response from the external API (for audit trail) */
|
||||
@Column(name = "raw_response", columnDefinition = "TEXT")
|
||||
private String rawResponse;
|
||||
|
||||
/** CLIENT_REGISTRATION, MANUAL_CHECK, API */
|
||||
@Column(name = "lookup_source", length = 50)
|
||||
private String lookupSource;
|
||||
|
||||
@Column(name = "client_id")
|
||||
private Long clientId;
|
||||
|
||||
@Column(name = "looked_up_on", nullable = false)
|
||||
private OffsetDateTime lookedUpOn;
|
||||
|
||||
protected MobileLookupLog() {}
|
||||
|
||||
public MobileLookupLog(final String mobileNumber, final String lookupStatus,
|
||||
final boolean suspected, final boolean disconnected,
|
||||
final String responseMessage, final String rawResponse,
|
||||
final String lookupSource, final Long clientId) {
|
||||
this.mobileNumber = mobileNumber;
|
||||
this.lookupStatus = lookupStatus;
|
||||
this.suspected = suspected;
|
||||
this.disconnected = disconnected;
|
||||
this.responseMessage = responseMessage;
|
||||
this.rawResponse = rawResponse;
|
||||
this.lookupSource = lookupSource;
|
||||
this.clientId = clientId;
|
||||
this.lookedUpOn = OffsetDateTime.now();
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.mobilelookup.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface MobileLookupLogRepository extends JpaRepository<MobileLookupLog, Long> {
|
||||
|
||||
@Query("SELECT l FROM MobileLookupLog l ORDER BY l.lookedUpOn DESC")
|
||||
List<MobileLookupLog> findAllOrderByLookedUpOnDesc();
|
||||
|
||||
@Query("SELECT l FROM MobileLookupLog l WHERE l.mobileNumber = :mobileNumber ORDER BY l.lookedUpOn DESC")
|
||||
List<MobileLookupLog> findByMobileNumber(@Param("mobileNumber") String mobileNumber);
|
||||
|
||||
@Query("SELECT l FROM MobileLookupLog l WHERE l.lookupStatus = :status ORDER BY l.lookedUpOn DESC")
|
||||
List<MobileLookupLog> findByLookupStatus(@Param("status") String status);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.sanctions.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Represents an alias (also-known-as) name for a sanctions list entry.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_sanctions_alias")
|
||||
public class SanctionsAlias extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "sanctions_entry_id", nullable = false)
|
||||
private SanctionsEntry sanctionsEntry;
|
||||
|
||||
@Column(name = "alias_quality", length = 50)
|
||||
private String aliasQuality;
|
||||
|
||||
@Column(name = "alias_name", nullable = false, length = 500)
|
||||
private String aliasName;
|
||||
|
||||
protected SanctionsAlias() {}
|
||||
|
||||
public SanctionsAlias(final String aliasQuality, final String aliasName) {
|
||||
this.aliasQuality = aliasQuality;
|
||||
this.aliasName = aliasName;
|
||||
}
|
||||
|
||||
public void setSanctionsEntry(SanctionsEntry sanctionsEntry) {
|
||||
this.sanctionsEntry = sanctionsEntry;
|
||||
}
|
||||
|
||||
public SanctionsEntry getSanctionsEntry() { return sanctionsEntry; }
|
||||
public String getAliasQuality() { return aliasQuality; }
|
||||
public String getAliasName() { return aliasName; }
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* 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.sanctions.domain;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Represents an individual or entity from the UN Security Council consolidated sanctions list.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_sanctions_entry")
|
||||
public class SanctionsEntry extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@Column(name = "data_id", nullable = false, length = 50)
|
||||
private String dataId;
|
||||
|
||||
/** INDIVIDUAL or ENTITY */
|
||||
@Column(name = "entry_type", nullable = false, length = 20)
|
||||
private String entryType;
|
||||
|
||||
@Column(name = "first_name", length = 255)
|
||||
private String firstName;
|
||||
|
||||
@Column(name = "second_name", length = 255)
|
||||
private String secondName;
|
||||
|
||||
@Column(name = "third_name", length = 255)
|
||||
private String thirdName;
|
||||
|
||||
@Column(name = "fourth_name", length = 255)
|
||||
private String fourthName;
|
||||
|
||||
@Column(name = "full_name", length = 500)
|
||||
private String fullName;
|
||||
|
||||
@Column(name = "un_list_type", length = 255)
|
||||
private String unListType;
|
||||
|
||||
@Column(name = "reference_number", length = 255)
|
||||
private String referenceNumber;
|
||||
|
||||
@Column(name = "listed_on")
|
||||
private LocalDate listedOn;
|
||||
|
||||
@Column(name = "gender", length = 20)
|
||||
private String gender;
|
||||
|
||||
@Column(name = "nationality", length = 500)
|
||||
private String nationality;
|
||||
|
||||
@Column(name = "comments", columnDefinition = "TEXT")
|
||||
private String comments;
|
||||
|
||||
@Column(name = "designation", columnDefinition = "TEXT")
|
||||
private String designation;
|
||||
|
||||
@Column(name = "date_of_birth", length = 500)
|
||||
private String dateOfBirth;
|
||||
|
||||
@Column(name = "place_of_birth", length = 500)
|
||||
private String placeOfBirth;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean active = true;
|
||||
|
||||
@OneToMany(mappedBy = "sanctionsEntry", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private List<SanctionsAlias> aliases = new ArrayList<>();
|
||||
|
||||
protected SanctionsEntry() {}
|
||||
|
||||
public SanctionsEntry(final String dataId, final String entryType, final String firstName,
|
||||
final String secondName, final String thirdName, final String fourthName,
|
||||
final String unListType, final String referenceNumber, final LocalDate listedOn,
|
||||
final String gender, final String nationality, final String comments,
|
||||
final String designation, final String dateOfBirth, final String placeOfBirth) {
|
||||
this.dataId = dataId;
|
||||
this.entryType = entryType;
|
||||
this.firstName = firstName;
|
||||
this.secondName = secondName;
|
||||
this.thirdName = thirdName;
|
||||
this.fourthName = fourthName;
|
||||
this.fullName = buildFullName(firstName, secondName, thirdName, fourthName);
|
||||
this.unListType = unListType;
|
||||
this.referenceNumber = referenceNumber;
|
||||
this.listedOn = listedOn;
|
||||
this.gender = gender;
|
||||
this.nationality = nationality;
|
||||
this.comments = comments;
|
||||
this.designation = designation;
|
||||
this.dateOfBirth = dateOfBirth;
|
||||
this.placeOfBirth = placeOfBirth;
|
||||
}
|
||||
|
||||
private static String buildFullName(String... parts) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String part : parts) {
|
||||
if (part != null && !part.isBlank()) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(part.trim());
|
||||
}
|
||||
}
|
||||
return sb.length() > 0 ? sb.toString() : null;
|
||||
}
|
||||
|
||||
public void addAlias(SanctionsAlias alias) {
|
||||
aliases.add(alias);
|
||||
alias.setSanctionsEntry(this);
|
||||
}
|
||||
|
||||
// ── Accessors ─────────────────────────────────────────────────────────────
|
||||
|
||||
public String getDataId() { return dataId; }
|
||||
public String getEntryType() { return entryType; }
|
||||
public String getFirstName() { return firstName; }
|
||||
public String getSecondName() { return secondName; }
|
||||
public String getThirdName() { return thirdName; }
|
||||
public String getFourthName() { return fourthName; }
|
||||
public String getFullName() { return fullName; }
|
||||
public String getUnListType() { return unListType; }
|
||||
public String getReferenceNumber() { return referenceNumber; }
|
||||
public LocalDate getListedOn() { return listedOn; }
|
||||
public String getGender() { return gender; }
|
||||
public String getNationality() { return nationality; }
|
||||
public String getComments() { return comments; }
|
||||
public String getDesignation() { return designation; }
|
||||
public String getDateOfBirth() { return dateOfBirth; }
|
||||
public String getPlaceOfBirth() { return placeOfBirth; }
|
||||
public boolean isActive() { return active; }
|
||||
public List<SanctionsAlias> getAliases() { return aliases; }
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.sanctions.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface SanctionsEntryRepository extends JpaRepository<SanctionsEntry, Long> {
|
||||
|
||||
@Query("SELECT s FROM SanctionsEntry s WHERE s.active = true AND LOWER(s.fullName) LIKE LOWER(CONCAT('%', :name, '%'))")
|
||||
List<SanctionsEntry> searchByFullName(@Param("name") String name);
|
||||
|
||||
@Query("SELECT DISTINCT s FROM SanctionsEntry s LEFT JOIN s.aliases a WHERE s.active = true " +
|
||||
"AND (LOWER(s.fullName) LIKE LOWER(CONCAT('%', :name, '%')) " +
|
||||
"OR LOWER(a.aliasName) LIKE LOWER(CONCAT('%', :name, '%')))")
|
||||
List<SanctionsEntry> searchByNameOrAlias(@Param("name") String name);
|
||||
|
||||
boolean existsByDataId(String dataId);
|
||||
|
||||
void deleteAllByActiveTrue();
|
||||
|
||||
long countByActiveTrue();
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.sanctions.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.OffsetDateTime;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Activity log for each sanctions screening request.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_sanctions_screening_log")
|
||||
public class SanctionsScreeningLog extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@Column(name = "screened_name", nullable = false, length = 500)
|
||||
private String screenedName;
|
||||
|
||||
/** FLAGGED or CLEAR */
|
||||
@Column(name = "screening_result", nullable = false, length = 20)
|
||||
private String screeningResult;
|
||||
|
||||
@Column(name = "match_count", nullable = false)
|
||||
private Integer matchCount;
|
||||
|
||||
@Column(name = "matched_entry_ids", length = 1000)
|
||||
private String matchedEntryIds;
|
||||
|
||||
@Column(name = "matched_names", length = 2000)
|
||||
private String matchedNames;
|
||||
|
||||
/** CLIENT_CREATION, MANUAL_CHECK, API */
|
||||
@Column(name = "screening_source", length = 50)
|
||||
private String screeningSource;
|
||||
|
||||
@Column(name = "client_id")
|
||||
private Long clientId;
|
||||
|
||||
@Column(name = "ip_address", length = 50)
|
||||
private String ipAddress;
|
||||
|
||||
@Column(name = "screened_on", nullable = false)
|
||||
private OffsetDateTime screenedOn;
|
||||
|
||||
protected SanctionsScreeningLog() {}
|
||||
|
||||
public SanctionsScreeningLog(final String screenedName, final String screeningResult,
|
||||
final Integer matchCount, final String matchedEntryIds, final String matchedNames,
|
||||
final String screeningSource, final Long clientId, final String ipAddress) {
|
||||
this.screenedName = screenedName;
|
||||
this.screeningResult = screeningResult;
|
||||
this.matchCount = matchCount;
|
||||
this.matchedEntryIds = matchedEntryIds;
|
||||
this.matchedNames = matchedNames;
|
||||
this.screeningSource = screeningSource;
|
||||
this.clientId = clientId;
|
||||
this.ipAddress = ipAddress;
|
||||
this.screenedOn = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
// ── Accessors ─────────────────────────────────────────────────────────────
|
||||
|
||||
public String getScreenedName() { return screenedName; }
|
||||
public String getScreeningResult() { return screeningResult; }
|
||||
public Integer getMatchCount() { return matchCount; }
|
||||
public String getMatchedEntryIds() { return matchedEntryIds; }
|
||||
public String getMatchedNames() { return matchedNames; }
|
||||
public String getScreeningSource() { return screeningSource; }
|
||||
public Long getClientId() { return clientId; }
|
||||
public String getIpAddress() { return ipAddress; }
|
||||
public OffsetDateTime getScreenedOn() { return screenedOn; }
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.sanctions.domain;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface SanctionsScreeningLogRepository extends JpaRepository<SanctionsScreeningLog, Long> {
|
||||
|
||||
@Query("SELECT l FROM SanctionsScreeningLog l ORDER BY l.screenedOn DESC")
|
||||
List<SanctionsScreeningLog> findAllOrderByScreenedOnDesc();
|
||||
|
||||
@Query("SELECT l FROM SanctionsScreeningLog l WHERE l.screeningResult = :result ORDER BY l.screenedOn DESC")
|
||||
List<SanctionsScreeningLog> findByScreeningResult(@Param("result") String result);
|
||||
|
||||
@Query("SELECT l FROM SanctionsScreeningLog l WHERE l.screenedOn >= :from AND l.screenedOn <= :to ORDER BY l.screenedOn DESC")
|
||||
List<SanctionsScreeningLog> findByDateRange(@Param("from") OffsetDateTime from, @Param("to") OffsetDateTime to);
|
||||
|
||||
@Query("SELECT l FROM SanctionsScreeningLog l WHERE LOWER(l.screenedName) LIKE LOWER(CONCAT('%', :name, '%')) ORDER BY l.screenedOn DESC")
|
||||
List<SanctionsScreeningLog> findByScreenedNameContaining(@Param("name") String name);
|
||||
}
|
||||
Reference in New Issue
Block a user