From c8a8560d426f30a9c0453295cdf97d6a5610bf3a Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Thu, 6 Aug 2026 20:46:57 +0530 Subject: [PATCH] fix(stoptb): fix RMNCH VanID placeholder-zero and id/VanSerialNo collision - RMNCHBeneficiaryDetailsRmnch: mobile sends VanID=0 as a placeholder (not null) for a fresh record - the existing '== null' check never caught it, leaving the real vanID from Redis unapplied. Now checks for both null and 0. - Both RMNCHBeneficiaryDetailsRmnch and RMNCHHouseHoldDetails map their VanSerialNo column to a Java field literally named 'id' with no @SerializedName - any incoming JSON payload that happens to carry its own 'id' key collides with it during Gson deserialization, silently overwriting the intended VanSerialNo with whatever unrelated value the client sent (observed live: every row stuck at VanSerialNo=1). Added updateVanSerialNo() to both repos and call it after each saveAll(), same pattern already used for i_beneficiaryimage/i_beneficiaryaddress in IdentityService.createIdentity(). - Left RMNCHCBACdetails/RMNCHBornBirthDetails alone despite sharing the same id-collision pattern - not part of the Stop TB flow, zero rows in practice, out of scope for this fix. Co-Authored-By: Claude Sonnet 5 --- .../repo/rmnch/RMNCHBeneficiaryDetailsRmnchRepo.java | 11 +++++++++++ .../repo/rmnch/RMNCHHouseHoldDetailsRepo.java | 11 +++++++++++ .../service/rmnch/RmnchDataSyncServiceImpl.java | 12 +++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHBeneficiaryDetailsRmnchRepo.java b/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHBeneficiaryDetailsRmnchRepo.java index 52916f31..cc36e5b4 100644 --- a/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHBeneficiaryDetailsRmnchRepo.java +++ b/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHBeneficiaryDetailsRmnchRepo.java @@ -24,10 +24,12 @@ import java.math.BigInteger; import java.util.List; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import com.iemr.common.identity.data.rmnch.RMNCHBeneficiaryDetailsRmnch; @@ -39,4 +41,13 @@ public RMNCHBeneficiaryDetailsRmnch getByIdAndVanID(@Param("vanSerialNo") BigInt @Query(" SELECT t FROM RMNCHBeneficiaryDetailsRmnch t WHERE t.BenRegId =:benRegID ") public List getByRegID(@Param("benRegID") BigInteger benRegId); + + // The Java field bound to the VanSerialNo column is literally named `id` with no + // @SerializedName - any incoming JSON that happens to carry its own "id" key (e.g. a + // client-side list-item id) collides with it during Gson deserialization and silently + // overwrites the intended VanSerialNo value. Force it back to the row's own PK after save. + @Transactional + @Modifying + @Query("UPDATE RMNCHBeneficiaryDetailsRmnch t SET t.id = :id WHERE t.beneficiaryDetails_RmnchId = :id") + void updateVanSerialNo(@Param("id") BigInteger id); } diff --git a/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHHouseHoldDetailsRepo.java b/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHHouseHoldDetailsRepo.java index 76490cbd..b8e0fc4b 100644 --- a/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHHouseHoldDetailsRepo.java +++ b/src/main/java/com/iemr/common/identity/repo/rmnch/RMNCHHouseHoldDetailsRepo.java @@ -21,10 +21,12 @@ */ package com.iemr.common.identity.repo.rmnch; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import com.iemr.common.identity.data.rmnch.RMNCHHouseHoldDetails; @@ -37,4 +39,13 @@ public interface RMNCHHouseHoldDetailsRepo extends CrudRepository getByHouseHoldID(@Param("houseoldId") long houseoldId); + + // The Java field bound to the VanSerialNo column is literally named `id` with no + // @SerializedName - any incoming JSON that happens to carry its own "id" key collides + // with it during Gson deserialization and silently overwrites the intended VanSerialNo + // value. Force it back to the row's own PK after save. + @Transactional + @Modifying + @Query("UPDATE RMNCHHouseHoldDetails t SET t.id = :id WHERE t.houseHoldDetailsId = :id") + void updateVanSerialNo(@Param("id") Long id); } diff --git a/src/main/java/com/iemr/common/identity/service/rmnch/RmnchDataSyncServiceImpl.java b/src/main/java/com/iemr/common/identity/service/rmnch/RmnchDataSyncServiceImpl.java index 27bc3cbf..57e76a8d 100644 --- a/src/main/java/com/iemr/common/identity/service/rmnch/RmnchDataSyncServiceImpl.java +++ b/src/main/java/com/iemr/common/identity/service/rmnch/RmnchDataSyncServiceImpl.java @@ -255,7 +255,9 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex } obj.setRelatedBeneficiaryIdsDB(sb.toString()); } - if (obj.getVanID() == null && vanID != null) { + // Mobile sends VanID=0 as a placeholder (not null) for a fresh record — + // `== null` alone never catches it, leaving the placeholder in place. + if ((obj.getVanID() == null || obj.getVanID() == 0) && vanID != null) { obj.setVanID(vanID); obj.setParkingPlaceID(parkingPlaceID); } @@ -297,6 +299,10 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex List benDetailsOriginalList = new ArrayList<>(benDetailsExtraList); benDetailsExtraList = (ArrayList) rMNCHBeneficiaryDetailsRmnchRepo .saveAll(benDetailsExtraList); + // The `id`/VanSerialNo field is Gson-collision-prone (see repo javadoc) — + // force it back to each row's own PK after save. + benDetailsExtraList.forEach((n) -> rMNCHBeneficiaryDetailsRmnchRepo + .updateVanSerialNo(n.getBeneficiaryDetails_RmnchId())); benDetailsExtraList.forEach((n) -> beneficiaryDetailsIds.add(n.getId())); // update beneficiary data in i_beneficiarydetails table @@ -413,6 +419,10 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex } houseHoldList = (ArrayList) rMNCHHouseHoldDetailsRepo .saveAll(houseHoldList); + // The `id`/VanSerialNo field is Gson-collision-prone (see repo javadoc) — + // force it back to each row's own PK after save. + houseHoldList.forEach((n) -> rMNCHHouseHoldDetailsRepo + .updateVanSerialNo(n.getHouseHoldDetailsId())); // success response houseHoldList.forEach((n) -> houseHoldDetailsIds.add(n.getId())); }