Add Client CKYC backend module with all source, migrations, and static resources

Includes CKYC batch processing, KYC proof types, non-CKYC client APIs,
ReKYC handler, SFTP upload service, and DB migrations (0250-0255).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-04-16 13:42:21 +05:30
co-authored by Claude Opus 4.6
parent 011239f399
commit b4429d04b0
38 changed files with 3103 additions and 12 deletions
@@ -665,6 +665,24 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder completeRekycClientKyc(final Long clientId, final Long kycId) {
this.actionName = "COMPLETEREKYC";
this.entityName = "CLIENTKYC";
this.entityId = kycId;
this.clientId = clientId;
this.href = "/clients/" + clientId + "/kyc/" + kycId + "?command=complete-rekyc";
return this;
}
public CommandWrapperBuilder uploadCkycClientKyc(final Long clientId, final Long kycId) {
this.actionName = "UPLOADCKYC";
this.entityName = "CLIENTKYC";
this.entityId = kycId;
this.clientId = clientId;
this.href = "/clients/" + clientId + "/kyc/" + kycId + "?command=upload-ckyc";
return this;
}
// ── Form 60 ──────────────────────────────────────────────────────────────
public CommandWrapperBuilder createClientForm60(final Long clientId) {
@@ -139,6 +139,12 @@ public class ClientKyc extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Column(name = "din_verified_on")
private LocalDate dinVerifiedOn;
@Column(name = "is_director", nullable = false)
private boolean isDirector;
@Column(name = "din_mca_response", columnDefinition = "longtext")
private String dinMcaResponse;
@Column(name = "incorporation_date")
private LocalDate incorporationDate;
@@ -202,6 +208,14 @@ public class ClientKyc extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Column(name = "ckyc_number", length = 14)
private String ckycNumber;
/** Date on which the CKYC record was pushed to CERSAI (SFTP batch). */
@Column(name = "ckyc_uploaded_on")
private LocalDate ckycUploadedOn;
/** Our internal batch id ("BATCH-yyyy-MM-dd-NNN") this KYC was shipped in. */
@Column(name = "ckyc_batch_id", length = 50)
private String ckycBatchId;
// ── Lifecycle dates ───────────────────────────────────────────────────────
@Column(name = "initiated_on")
private LocalDate initiatedOn;
@@ -427,6 +441,17 @@ public class ClientKyc extends AbstractAuditableWithUTCDateTimeCustom<Long> {
this.expiryDate = null;
}
/**
* Records a CKYC upload to CERSAI. Sets the 14-digit CKYC number, the upload date and the
* optional batch id. Does <b>not</b> change {@code kycStatus} — upload is orthogonal to
* verification and is normally performed only after the KYC is already VERIFIED.
*/
public void markCkycUploaded(final String ckycNumber, final String batchId) {
this.ckycNumber = StringUtils.defaultIfEmpty(ckycNumber, null);
this.ckycBatchId = StringUtils.defaultIfEmpty(batchId, null);
this.ckycUploadedOn = LocalDate.now();
}
/** Called after successful 3rd-party verification of all required documents. */
public void markVerified(final String ckycNumber) {
this.kycStatus = ClientKycStatus.VERIFIED.getValue();
@@ -493,6 +518,14 @@ public class ClientKyc extends AbstractAuditableWithUTCDateTimeCustom<Long> {
this.dinVerifiedOn = LocalDate.now();
}
public void markDinVerified(final String dinNumber, final String mcaResponseJson) {
this.dinNumber = dinNumber;
this.dinVerified = true;
this.dinVerifiedOn = LocalDate.now();
this.isDirector = true;
this.dinMcaResponse = mcaResponseJson;
}
/**
* Checks if all required verifications are complete for this KYC type and auto-promotes
* to VERIFIED status if so.
@@ -609,6 +642,14 @@ public class ClientKyc extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return ckycNumber;
}
public LocalDate getCkycUploadedOn() {
return ckycUploadedOn;
}
public String getCkycBatchId() {
return ckycBatchId;
}
public LocalDate getInitiatedOn() {
return initiatedOn;
}
@@ -657,6 +698,14 @@ public class ClientKyc extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return dinVerifiedOn;
}
public boolean isDirector() {
return isDirector;
}
public String getDinMcaResponse() {
return dinMcaResponse;
}
public LocalDate getIncorporationDate() {
return incorporationDate;
}