Client uniqueness pre-save check + seed all Indian states & UTs
Pre-save validator (ClientWritePlatformServiceJpaRepositoryImpl.validateClientUniqueness) rejects duplicate mobile / email / PAN / Aadhaar (masked) / GSTIN before saveAndFlush and before the LincPay KYC API call. Avoids burning vendor credits on identity-duplicate records and gives the UI a clean field-level 403 instead of waiting for the DB constraint. Splits the new repository finders into "create" and "excluding-self" variants so a nullable Long parameter never reaches PostgreSQL's parser (same bigint <> varchar trap that broke the family-member POST earlier). Also seeds the full set of 28 Indian States + 8 UTs as STATE code_values (migration 0297). Previously only MAHARASHTRA / MADHYA_PRADESH / DELHI existed and the names were UPPER_SNAKE_CASE; the postal PIN-lookup service returns "Madhya Pradesh" so any state other than Maharashtra/Delhi silently failed to resolve to m_address.state_province_id. Renames the 3 existing rows in place (preserves their ids/FKs) and inserts the missing 33 with idempotent preConditions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
026e5fb875
commit
8b4351013e
+27
@@ -35,4 +35,31 @@ public interface ClientKycRepository extends JpaRepository<ClientKyc, Long> {
|
||||
|
||||
@Query("SELECT k FROM ClientKyc k WHERE k.kycStatus = :status")
|
||||
List<ClientKyc> findAllByStatus(@Param("status") Integer status);
|
||||
|
||||
// ── Pre-KYC uniqueness probes ───────────────────────────────────────────
|
||||
// Why: stop a second client from being created with the same PAN / Aadhaar / GSTIN BEFORE the
|
||||
// external KYC API is called, so we don't burn vendor calls on duplicate-identity records. PAN
|
||||
// and GSTIN match exact upper-cased; Aadhaar is compared masked-form (only last 4 are stored).
|
||||
// Two variants per field — one for create (no row to exclude), one for update (exclude self) —
|
||||
// to avoid PostgreSQL's bigint <> varchar parse error on a null Long parameter.
|
||||
@Query("SELECT CASE WHEN COUNT(k) > 0 THEN true ELSE false END FROM ClientKyc k WHERE UPPER(k.panNumber) = UPPER(:panNumber)")
|
||||
boolean existsByPanNumber(@Param("panNumber") String panNumber);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(k) > 0 THEN true ELSE false END FROM ClientKyc k "
|
||||
+ "WHERE UPPER(k.panNumber) = UPPER(:panNumber) AND k.client.id <> :excludeClientId")
|
||||
boolean existsByPanNumberExcluding(@Param("panNumber") String panNumber, @Param("excludeClientId") Long excludeClientId);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(k) > 0 THEN true ELSE false END FROM ClientKyc k WHERE k.aadhaarNumberMasked = :aadhaarMasked")
|
||||
boolean existsByAadhaarMasked(@Param("aadhaarMasked") String aadhaarMasked);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(k) > 0 THEN true ELSE false END FROM ClientKyc k "
|
||||
+ "WHERE k.aadhaarNumberMasked = :aadhaarMasked AND k.client.id <> :excludeClientId")
|
||||
boolean existsByAadhaarMaskedExcluding(@Param("aadhaarMasked") String aadhaarMasked, @Param("excludeClientId") Long excludeClientId);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(k) > 0 THEN true ELSE false END FROM ClientKyc k WHERE UPPER(k.gstinNumber) = UPPER(:gstinNumber)")
|
||||
boolean existsByGstinNumber(@Param("gstinNumber") String gstinNumber);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(k) > 0 THEN true ELSE false END FROM ClientKyc k "
|
||||
+ "WHERE UPPER(k.gstinNumber) = UPPER(:gstinNumber) AND k.client.id <> :excludeClientId")
|
||||
boolean existsByGstinNumberExcluding(@Param("gstinNumber") String gstinNumber, @Param("excludeClientId") Long excludeClientId);
|
||||
}
|
||||
|
||||
+21
@@ -61,4 +61,25 @@ public interface ClientRepository extends JpaRepository<Client, Long>, JpaSpecif
|
||||
*/
|
||||
@Query("SELECT COUNT(c) FROM Client c WHERE c.cifNumber LIKE CONCAT(:prefix, '%')")
|
||||
long countByCifNumberPrefix(@Param("prefix") String prefix);
|
||||
|
||||
// ── Pre-save uniqueness probes ───────────────────────────────────────────
|
||||
// Why: DB has UNIQUE on mobile_no / email_address, but we want to fail fast with a clear
|
||||
// field-level error BEFORE saveAndFlush — and importantly before the external KYC API call.
|
||||
// Two variants on each: create-time has no row to exclude; update-time excludes the row being
|
||||
// edited so it doesn't conflict with itself. Splitting avoids the bigint <> varchar JPQL bug
|
||||
// PostgreSQL throws when a null Long parameter is parsed as varchar at prepare-time.
|
||||
@Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM Client c WHERE c.mobileNo = :mobileNo")
|
||||
boolean existsByMobileNo(@Param("mobileNo") String mobileNo);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM Client c "
|
||||
+ "WHERE c.mobileNo = :mobileNo AND c.id <> :excludeClientId")
|
||||
boolean existsByMobileNoExcluding(@Param("mobileNo") String mobileNo, @Param("excludeClientId") Long excludeClientId);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM Client c "
|
||||
+ "WHERE LOWER(c.emailAddress) = LOWER(:emailAddress)")
|
||||
boolean existsByEmailAddress(@Param("emailAddress") String emailAddress);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM Client c "
|
||||
+ "WHERE LOWER(c.emailAddress) = LOWER(:emailAddress) AND c.id <> :excludeClientId")
|
||||
boolean existsByEmailAddressExcluding(@Param("emailAddress") String emailAddress, @Param("excludeClientId") Long excludeClientId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user