diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index e32dc366..1ce43664 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -22,6 +22,9 @@ fhir-url=@env.FHIR_API@ # Redis Config spring.redis.host=@env.REDIS_HOST@ +# Stop TB: when true, RMNCH data sync fails with an error if camp (vanID) is not configured +stoptb.enforce.vanid=@env.STOPTB_ENFORCE_VANID@ + cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@ # Elasticsearch Configuration diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 07bd53cc..36735700 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -22,6 +22,9 @@ fhir-url=${FHIR_API} # Redis Config spring.redis.host=${REDIS_HOST} +# Stop TB: when true, RMNCH data sync fails with an error if camp (vanID) is not configured +stoptb.enforce.vanid=${STOPTB_ENFORCE_VANID} + cors.allowed-origins=${CORS_ALLOWED_ORIGINS} # Elasticsearch Configuration diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index b78483f2..dd235c95 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -17,6 +17,11 @@ fhir-url=http://localhost:8093/ # Redis Config spring.redis.host=localhost + +# Stop TB: when true, RMNCH data sync fails with an error if camp (vanID) is not +# configured instead of silently skipping vanID stamping +stoptb.enforce.vanid=false + cors.allowed-origins=http://localhost:* # Elasticsearch Configuration diff --git a/src/main/java/com/iemr/common/identity/controller/IdentityController.java b/src/main/java/com/iemr/common/identity/controller/IdentityController.java index fc894761..8d015bd1 100644 --- a/src/main/java/com/iemr/common/identity/controller/IdentityController.java +++ b/src/main/java/com/iemr/common/identity/controller/IdentityController.java @@ -608,6 +608,11 @@ public String createIdentity(@Param(value = "{\r\n" + " \"eventTypeName\": \"St + " \"createdDate\": \"Timestamp\"\r\n" + " \"faceEmbedding\": [\"Float\"]\r\n" + "}") @RequestBody String identityData) throws IEMRException { logger.info("IdentityController.createIdentity - start"); + // Bare Gson matches Common-API's RegisterBenificiaryServiceImpl, which also + // serializes the outgoing identity payload with a bare new Gson(). dob relies + // on this symmetric default format; gpsTimestamp is still parsed correctly via + // its field-level @JsonAdapter(GpsTimestampAdapter.class) on Address, which + // works regardless of which Gson instance performs the parse. IdentityDTO identity = new Gson().fromJson(identityData, IdentityDTO.class); logger.info("identity hit: " + identity); BeneficiaryCreateResp map; diff --git a/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java b/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java index 1fb1b66d..814d2c73 100644 --- a/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java +++ b/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java @@ -34,6 +34,8 @@ import jakarta.persistence.Transient; import com.google.gson.annotations.Expose; +import com.google.gson.annotations.JsonAdapter; +import com.iemr.common.identity.mapper.GpsTimestampAdapter; import lombok.Data; @@ -390,6 +392,9 @@ public class RMNCHBeneficiaryDetailsRmnch { @Expose @Transient private String addressLine3; + @Expose + @Transient + private String pinCode; // ---------------------------------------------- @@ -557,4 +562,45 @@ public class RMNCHBeneficiaryDetailsRmnch { @Expose @Transient private String familyId; + + // Anthropometry fields sent by Stop TB mobile app via beneficiaryDetails payload. + // i_beneficiarydetails_rmnch has no these columns — stored in i_beneficiarydetails.otherFields instead. + @Expose + @Transient + private Double height; + @Expose + @Transient + private Double weight; + @Expose + @Transient + private Double bmi; + @Expose + @Transient + private Double temperature; // stored as "temperatureValue" in otherFields to match getBeneficiaryData key + + @Expose + @Column(name = "gpsLatitude") + private Double gpsLatitude; + + @Expose + @Column(name = "gpsLongitude") + private Double gpsLongitude; + + @Expose + @Column(name = "digipin") + private String digipin; + + @Expose + @Column(name = "gpsTimestamp") + @JsonAdapter(GpsTimestampAdapter.class) + private Timestamp gpsTimestamp; + + @Expose + @Column(name = "isGpsUnavailable", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0") + private Boolean isGpsUnavailable = false; + + @Expose + @Column(name = "gpsUnavailableReason") + private String gpsUnavailableReason; + } diff --git a/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHHouseHoldDetails.java b/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHHouseHoldDetails.java index 8aa8ed47..7000f6d0 100644 --- a/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHHouseHoldDetails.java +++ b/src/main/java/com/iemr/common/identity/data/rmnch/RMNCHHouseHoldDetails.java @@ -1,24 +1,24 @@ /* -* AMRIT – Accessible Medical Records via Integrated Technology -* Integrated EHR (Electronic Health Records) Solution -* -* Copyright (C) "Piramal Swasthya Management and Research Institute" -* -* This file is part of AMRIT. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ package com.iemr.common.identity.data.rmnch; import java.sql.Timestamp; @@ -31,11 +31,14 @@ import jakarta.persistence.Table; import com.google.gson.annotations.Expose; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.iemr.common.identity.mapper.GpsTimestampAdapter; import lombok.Data; /** - * + * * @author de40034072 * */ @@ -359,4 +362,31 @@ public class RMNCHHouseHoldDetails { @Column(name = "mohallaName") private String mohallaName; + @Expose + @SerializedName("latitude") + @Column(name = "gpsLatitude") + private Double gpsLatitude; + + @Expose + @SerializedName("longitude") + @Column(name = "gpsLongitude") + private Double gpsLongitude; + + @Expose + @Column(name = "digipin") + private String digipin; + + @Expose + @Column(name = "gpsTimestamp") + @JsonAdapter(GpsTimestampAdapter.class) + private Timestamp gpsTimestamp; + + @Expose + @Column(name = "isGpsUnavailable", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0") + private Boolean isGpsUnavailable = false; + + @Expose + @Column(name = "gpsUnavailableReason") + private String gpsUnavailableReason; + } diff --git a/src/main/java/com/iemr/common/identity/domain/Address.java b/src/main/java/com/iemr/common/identity/domain/Address.java index c37b4507..f355cbc8 100644 --- a/src/main/java/com/iemr/common/identity/domain/Address.java +++ b/src/main/java/com/iemr/common/identity/domain/Address.java @@ -23,6 +23,11 @@ import lombok.Data; +import java.sql.Timestamp; + +import com.google.gson.annotations.JsonAdapter; +import com.iemr.common.identity.mapper.GpsTimestampAdapter; + public @Data class Address { private String addrLine1; private String addrLine2; @@ -51,4 +56,11 @@ private Integer vanID; private Integer parkingPlaceID; + private Double gpsLatitude; + private Double gpsLongitude; + private String digipin; + @JsonAdapter(GpsTimestampAdapter.class) + private Timestamp gpsTimestamp; + private Boolean isGpsUnavailable; + private String gpsUnavailableReason; } diff --git a/src/main/java/com/iemr/common/identity/domain/MBeneficiaryaddress.java b/src/main/java/com/iemr/common/identity/domain/MBeneficiaryaddress.java index 0e2ab415..d5eaf7bd 100644 --- a/src/main/java/com/iemr/common/identity/domain/MBeneficiaryaddress.java +++ b/src/main/java/com/iemr/common/identity/domain/MBeneficiaryaddress.java @@ -1,24 +1,24 @@ /* -* AMRIT – Accessible Medical Records via Integrated Technology -* Integrated EHR (Electronic Health Records) Solution -* -* Copyright (C) "Piramal Swasthya Management and Research Institute" -* -* This file is part of AMRIT. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ package com.iemr.common.identity.domain; import java.io.Serializable; @@ -39,7 +39,7 @@ /** * The persistent class for the m_beneficiaryaddress database table. - * + * */ @Entity @Table(name = "i_beneficiaryaddress") @@ -260,6 +260,24 @@ public class MBeneficiaryaddress implements Serializable { // END OF new column added for data sync + @Column(name = "gpsLatitude") + private Double gpsLatitude; + + @Column(name = "gpsLongitude") + private Double gpsLongitude; + + @Column(name = "digipin") + private String digipin; + + @Column(name = "gpsTimestamp") + private Timestamp gpsTimestamp; + + @Column(name = "isGpsUnavailable", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0") + private Boolean isGpsUnavailable = false; + + @Column(name = "gpsUnavailableReason") + private String gpsUnavailableReason; + public MBeneficiaryaddress setCurrentAddress(Address address) { this.currAddrLine1 = address.getAddrLine1(); this.currAddrLine2 = address.getAddrLine2(); diff --git a/src/main/java/com/iemr/common/identity/mapper/GpsTimestampAdapter.java b/src/main/java/com/iemr/common/identity/mapper/GpsTimestampAdapter.java new file mode 100644 index 00000000..1f1fe2ff --- /dev/null +++ b/src/main/java/com/iemr/common/identity/mapper/GpsTimestampAdapter.java @@ -0,0 +1,116 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.common.identity.mapper; + +import java.io.IOException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +/** + * Parses the GPS capture timestamp (epoch millis, ISO-8601 with/without a literal + * 'Z', or the mobile client's "MMM dd, yyyy, h:mm:ss a" format). + * + * Attach with {@code @JsonAdapter(GpsTimestampAdapter.class)} directly on a + * gpsTimestamp field only. Do NOT register this globally on a GsonBuilder for + * Timestamp.class — that previously intercepted every Timestamp field in the + * request (including dob) and silently nulled it out whenever the client's + * format didn't match one of the patterns below. + */ +public class GpsTimestampAdapter extends TypeAdapter { + + private static final Logger logger = LoggerFactory.getLogger(GpsTimestampAdapter.class); + + private static final DateTimeFormatter ISO_WITH_Z = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private static final DateTimeFormatter ISO_NO_TZ = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); + private static final DateTimeFormatter CLIENT_DATE_FORMAT = + DateTimeFormatter.ofPattern("MMM dd, yyyy, h:mm:ss a", Locale.ENGLISH); + + @Override + public void write(JsonWriter out, Timestamp value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.getTime()); + } + } + + @Override + public Timestamp read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + if (in.peek() == JsonToken.NUMBER) { + return new Timestamp(in.nextLong()); + } + + String s = in.nextString(); + + // epoch millis as string + try { + return new Timestamp(Long.parseLong(s)); + } catch (NumberFormatException ignored) { + // not epoch millis, try the date formats below + } + // ISO 8601 with Z, e.g. "2021-06-18T00:00:00.000Z" + try { + return Timestamp.from(Instant.parse(s)); + } catch (Exception ignored) { + // not this format + } + // Mobile client format, e.g. "Jun 18, 2021, 5:30:00 AM" + try { + return Timestamp.valueOf(LocalDateTime.parse(s, CLIENT_DATE_FORMAT)); + } catch (Exception ignored) { + // not this format + } + // ISO with literal 'Z' pattern, e.g. "2021-06-18T00:00:00.000Z" parsed as local + try { + return Timestamp.valueOf(LocalDateTime.parse(s, ISO_WITH_Z)); + } catch (Exception ignored) { + // not this format + } + // ISO without timezone, e.g. "2021-06-18T00:00:00.000" (assume UTC) + try { + return Timestamp.from(LocalDateTime.parse(s, ISO_NO_TZ).toInstant(ZoneOffset.UTC)); + } catch (Exception ignored) { + // not this format + } + + logger.warn("GpsTimestampAdapter: unable to parse gpsTimestamp value '{}' with any known format; storing as null", s); + return null; + } +} diff --git a/src/main/java/com/iemr/common/identity/mapper/IdentityMapper.java b/src/main/java/com/iemr/common/identity/mapper/IdentityMapper.java index bc3ad686..765acb97 100644 --- a/src/main/java/com/iemr/common/identity/mapper/IdentityMapper.java +++ b/src/main/java/com/iemr/common/identity/mapper/IdentityMapper.java @@ -1,24 +1,24 @@ /* -* AMRIT – Accessible Medical Records via Integrated Technology -* Integrated EHR (Electronic Health Records) Solution -* -* Copyright (C) "Piramal Swasthya Management and Research Institute" -* -* This file is part of AMRIT. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ package com.iemr.common.identity.mapper; import java.sql.Timestamp; @@ -66,7 +66,7 @@ public interface IdentityMapper { MBeneficiarymapping identityDTOToMBeneficiarymapping(IdentityDTO dto); - + @Mapping(source = "defaultNo", target = "shareAnonymousWithGovt") @Mapping(source = "defaultNo", target = "shareAnonymousWithMedicalCommunity") @@ -93,7 +93,7 @@ public interface IdentityMapper { MBeneficiaryconsent identityDTOToDefaultMBeneficiaryconsent(IdentityDTO dto, Boolean defaultYes, Boolean defaultNo); - + // @Mapping(source = "dto.areaId", target = "areaId") // @Mapping(source = "dto.beneficiaryRegId", target = "beneficiaryRegID") @@ -144,7 +144,7 @@ public interface IdentityMapper { // @Mapping(source = "dto.vanID", target = "vanID") // @Mapping(source = "dto.parkingPlaceId", target = "parkingPlaceID") // MBeneficiarydetail identityDTOToMBeneficiarydetail(IdentityDTO dto); - + @Mapping(source = "benFamilyDTO.isEmergencyContact", target = "isEmergencyContact") @Mapping(source = "benFamilyDTO.relationshipToSelf", target = "relationshipToSelf") @@ -152,7 +152,7 @@ public interface IdentityMapper { @Mapping(source = "createdBy", target = "createdBy") @Mapping(source = "createdDate", target = "createdDate") MBeneficiaryfamilymapping identityDTOToMBeneficiaryfamilymapping(BenFamilyDTO benFamilyDTO, String createdBy, - Timestamp createdDate); + Timestamp createdDate); List identityDTOListToMBeneficiaryfamilymappingList(List list); @@ -210,6 +210,12 @@ MBeneficiaryfamilymapping identityDTOToMBeneficiaryfamilymapping(BenFamilyDTO be @Mapping(target = "currentAddress.village", source = "map.MBeneficiaryaddress.currVillage") @Mapping(target = "currentAddress.addressValue", source = "map.MBeneficiaryaddress.currAddressValue") @Mapping(target = "currentAddress.pinCode", source = "map.MBeneficiaryaddress.currPinCode") + @Mapping(target = "currentAddress.gpsLatitude", source = "map.MBeneficiaryaddress.gpsLatitude") + @Mapping(target = "currentAddress.gpsLongitude", source = "map.MBeneficiaryaddress.gpsLongitude") + @Mapping(target = "currentAddress.digipin", source = "map.MBeneficiaryaddress.digipin") + @Mapping(target = "currentAddress.gpsTimestamp", source = "map.MBeneficiaryaddress.gpsTimestamp") + @Mapping(target = "currentAddress.isGpsUnavailable", source = "map.MBeneficiaryaddress.isGpsUnavailable") + @Mapping(target = "currentAddress.gpsUnavailableReason", source = "map.MBeneficiaryaddress.gpsUnavailableReason") @Mapping(target = "emergencyAddress.addrLine1", source = "map.MBeneficiaryaddress.emerAddrLine1") @Mapping(target = "emergencyAddress.addrLine2", source = "map.MBeneficiaryaddress.emerAddrLine2") @Mapping(target = "emergencyAddress.addrLine3", source = "map.MBeneficiaryaddress.emerAddrLine3") @@ -296,13 +302,13 @@ MBeneficiaryfamilymapping identityDTOToMBeneficiaryfamilymapping(BenFamilyDTO be @Mapping(target = "beneficiaryDetails.title", source = "map.MBeneficiarydetail.title") @Mapping(target = "beneficiaryDetails.zoneId", source = "map.MBeneficiarydetail.zoneId") @Mapping(target = "contacts", expression = "java( map != null && map.getMBeneficiarycontact() != null && " - + "map.getMBeneficiarydetail() != null ? " - + "Phone.createContactList(map.getMBeneficiarycontact(), " - + "(benRegId != null ? benRegId.toString() : null), " - + "(map.getMBeneficiarydetail().getFirstName() != null ? map.getMBeneficiarydetail().getFirstName() : \"\") + \" \" + " - + "(map.getMBeneficiarydetail().getMiddleName() != null ? map.getMBeneficiarydetail().getMiddleName() : \"\") + \" \" + " - + "(map.getMBeneficiarydetail().getLastName() != null ? map.getMBeneficiarydetail().getLastName() : \"\") " - + ") : null)") + + "map.getMBeneficiarydetail() != null ? " + + "Phone.createContactList(map.getMBeneficiarycontact(), " + + "(map.getBenRegId() != null ? map.getBenRegId().toString() : null), " + + "(map.getMBeneficiarydetail().getFirstName() != null ? map.getMBeneficiarydetail().getFirstName() : \"\") + \" \" + " + + "(map.getMBeneficiarydetail().getMiddleName() != null ? map.getMBeneficiarydetail().getMiddleName() : \"\") + \" \" + " + + "(map.getMBeneficiarydetail().getLastName() != null ? map.getMBeneficiarydetail().getLastName() : \"\") " + + ") : null)") @Mapping(target = "permanentAddress.zoneID", source = "map.MBeneficiaryaddress.permZoneID") @Mapping(target = "permanentAddress.zoneName", source = "map.MBeneficiaryaddress.permZone") @@ -334,13 +340,13 @@ MBeneficiaryfamilymapping identityDTOToMBeneficiaryfamilymapping(BenFamilyDTO be @Mapping(target = "accountNo", source = "map.MBeneficiaryAccount.accountNo") @Mapping(target = "benAccountID", source = "map.benAccountID") @Mapping(target = "ageAtMarriage", expression = "java(map != null && map.getMBeneficiarydetail() != null ? " - + "MBeneficiarydetail.getAgeAtMarriageCalc(map.getMBeneficiarydetail().getDob(), " - + "map.getMBeneficiarydetail().getMarriageDate(), " - + "map.getMBeneficiarydetail().getAgeAtMarriage()) : null)") + + "MBeneficiarydetail.getAgeAtMarriageCalc(map.getMBeneficiarydetail().getDob(), " + + "map.getMBeneficiarydetail().getMarriageDate(), " + + "map.getMBeneficiarydetail().getAgeAtMarriage()) : null)") @Mapping(target = "marriageDate", expression = "java(map != null && map.getMBeneficiarydetail() != null ? " - + "MBeneficiarydetail.getMarriageDateCalc(map.getMBeneficiarydetail().getDob(), " - + "map.getMBeneficiarydetail().getMarriageDate(), " - + "map.getMBeneficiarydetail().getAgeAtMarriage()) : null)") + + "MBeneficiarydetail.getMarriageDateCalc(map.getMBeneficiarydetail().getDob(), " + + "map.getMBeneficiarydetail().getMarriageDate(), " + + "map.getMBeneficiarydetail().getAgeAtMarriage()) : null)") @Mapping(target = "literacyStatus", source = "map.MBeneficiarydetail.literacyStatus") @Mapping(target = "motherName", source = "map.MBeneficiarydetail.motherName") @@ -481,7 +487,7 @@ List mapToMBeneficiaryfamilymappingWithBenFamilyDTOList( @Mapping(source = "dto.createdDate", target = "createdDate") @Mapping(source = "dto.vanID", target = "vanID") @Mapping(source = "dto.parkingPlaceId", target = "parkingPlaceID") - // End + // End MBeneficiaryAccount identityDTOToMBeneficiaryAccount(IdentityDTO dto); @InheritInverseConfiguration @@ -490,7 +496,7 @@ List mapToMBeneficiaryfamilymappingWithBenFamilyDTOList( @Mapping(source = "dto.benImage", target = "benImage") @Mapping(source = "dto.agentName", target = "createdBy") @Mapping(source = "dto.createdDate", target = "createdDate") - + @Mapping(source = "dto.vanID", target = "vanID") @Mapping(source = "dto.parkingPlaceId", target = "parkingPlaceID") diff --git a/src/main/java/com/iemr/common/identity/mapper/InputMapper.java b/src/main/java/com/iemr/common/identity/mapper/InputMapper.java index 9a46ba4f..3549efc2 100644 --- a/src/main/java/com/iemr/common/identity/mapper/InputMapper.java +++ b/src/main/java/com/iemr/common/identity/mapper/InputMapper.java @@ -44,6 +44,10 @@ public class InputMapper private InputMapper() { + // Timestamp fields (including dob) use Gson's default parsing here, same as + // on vb/stoptb. The gpsTimestamp field on Address/RMNCH entities is parsed by + // GpsTimestampAdapter via a field-level @JsonAdapter annotation instead of a + // global registration, so it can't affect any other Timestamp field. builder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") // .excludeFieldsWithoutExposeAnnotation() .serializeNulls().setLongSerializationPolicy(LongSerializationPolicy.STRING); diff --git a/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java b/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java index 4e8bcc01..413298ba 100644 --- a/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java +++ b/src/main/java/com/iemr/common/identity/repo/BenDetailRepo.java @@ -148,6 +148,11 @@ int untagFamily(@Param("modifiedBy") String modifiedBy, @Param("vanSerialNo") Bi @Query("SELECT b FROM MBeneficiarydetail b WHERE b.familyId =:familyid ") List searchByFamilyId(@Param("familyid") String familyid); + @Transactional + @Modifying + @Query("UPDATE MBeneficiarydetail d SET d.otherFields = :otherFields WHERE d.mBeneficiarymapping.benRegId = :benRegId") + int updateOtherFieldsByBenRegId(@Param("benRegId") BigInteger benRegId, @Param("otherFields") String otherFields); + /** * Find complete beneficiary data by IDs from Elasticsearch */ diff --git a/src/main/java/com/iemr/common/identity/service/IdentityService.java b/src/main/java/com/iemr/common/identity/service/IdentityService.java index 90e660b9..bb1dd988 100644 --- a/src/main/java/com/iemr/common/identity/service/IdentityService.java +++ b/src/main/java/com/iemr/common/identity/service/IdentityService.java @@ -1,23 +1,23 @@ /* -* AMRIT – Accessible Medical Records via Integrated Technology -* Integrated EHR (Electronic Health Records) Solution -* -* Copyright (C) "Piramal Swasthya Management and Research Institute" -* -* This file is part of AMRIT. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. */ package com.iemr.common.identity.service; @@ -229,7 +229,7 @@ public List getBeneficiaries(IdentitySearchDTO searchDTO) if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null || list3.get(i).getBeneficiaryDetails().getFirstName() == null || !list3.get(i).getBeneficiaryDetails().getFirstName() - .equalsIgnoreCase(searchDTO.getFirstName())) { + .equalsIgnoreCase(searchDTO.getFirstName())) { list3.remove(i); i--; @@ -241,7 +241,7 @@ public List getBeneficiaries(IdentitySearchDTO searchDTO) if (list3.get(i) == null || list3.get(i).getBeneficiaryDetails() == null || list3.get(i).getBeneficiaryDetails().getLastName() == null || !list3.get(i).getBeneficiaryDetails().getLastName() - .equalsIgnoreCase(searchDTO.getLastName())) { + .equalsIgnoreCase(searchDTO.getLastName())) { list3.remove(i); i--; @@ -286,7 +286,7 @@ public List getBeneficiaries(IdentitySearchDTO searchDTO) if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null || list3.get(i).getCurrentAddress().getDistrictId() == null || !list3.get(i).getCurrentAddress().getDistrictId() - .equals(searchDTO.getCurrentAddress().getDistrictId())) { + .equals(searchDTO.getCurrentAddress().getDistrictId())) { list3.remove(i); i--; @@ -298,7 +298,7 @@ public List getBeneficiaries(IdentitySearchDTO searchDTO) if (list3.get(i) == null || list3.get(i).getCurrentAddress() == null || list3.get(i).getCurrentAddress().getVillageId() == null || !list3.get(i).getCurrentAddress().getVillageId() - .equals(searchDTO.getCurrentAddress().getVillageId())) { + .equals(searchDTO.getCurrentAddress().getVillageId())) { list3.remove(i); i--; @@ -515,7 +515,7 @@ public List getBeneficiariesByBenId(BigInteger benId) /** * - * @param BenRegId + * @param benRegId * @return */ public List getBeneficiariesByBenRegId(BigInteger benRegId) @@ -591,104 +591,104 @@ public List getBeneficiariesByPhoneNum(String phoneNum) } -/** - * Advanced search using Elasticsearch with fallback to database - */ -public Map advancedSearchBeneficiariesES( - String firstName, String middleName, String lastName, Integer genderId, java.util.Date dob, - Integer stateId, Integer districtId, Integer blockId, Integer villageId, - String fatherName, String spouseName, String maritalStatus, String phoneNumber, - String beneficiaryId, String healthId, String aadharNo, - Integer userId, String auth, Boolean is1097) throws Exception { - - try { - logger.info("IdentityService.advancedSearchBeneficiariesES - start"); - logger.info("ES enabled: {}", esEnabled); - - Map response = new HashMap<>(); - - if (esEnabled) { - logger.info("Using Elasticsearch for advanced search"); - - // Call Elasticsearch service - List> esResults = elasticsearchService.advancedSearch( - firstName, middleName, lastName, genderId, dob, stateId, districtId, - blockId, villageId, fatherName, spouseName, maritalStatus, phoneNumber, - beneficiaryId, healthId, aadharNo, userId - ); - - response.put("data", esResults); - response.put("count", esResults.size()); - response.put("source", "elasticsearch"); - - logger.info("ES returned {} results", esResults.size()); - - } else { - logger.info("ES disabled - using database for advanced search"); - - IdentitySearchDTO searchDTO = new IdentitySearchDTO(); - searchDTO.setFirstName(firstName); - searchDTO.setLastName(lastName); - searchDTO.setGenderId(genderId); - searchDTO.setDob(dob != null ? new Timestamp(dob.getTime()) : null); - searchDTO.setFatherName(fatherName); - searchDTO.setSpouseName(spouseName); - searchDTO.setContactNumber(phoneNumber); - - if (beneficiaryId != null && !beneficiaryId.trim().isEmpty()) { - try { - searchDTO.setBeneficiaryId(new BigInteger(beneficiaryId)); - } catch (NumberFormatException e) { - logger.warn("Invalid beneficiaryId format: {}", beneficiaryId); + /** + * Advanced search using Elasticsearch with fallback to database + */ + public Map advancedSearchBeneficiariesES( + String firstName, String middleName, String lastName, Integer genderId, Date dob, + Integer stateId, Integer districtId, Integer blockId, Integer villageId, + String fatherName, String spouseName, String maritalStatus, String phoneNumber, + String beneficiaryId, String healthId, String aadharNo, + Integer userId, String auth, Boolean is1097) throws Exception { + + try { + logger.info("IdentityService.advancedSearchBeneficiariesES - start"); + logger.info("ES enabled: {}", esEnabled); + + Map response = new HashMap<>(); + + if (esEnabled) { + logger.info("Using Elasticsearch for advanced search"); + + // Call Elasticsearch service + List> esResults = elasticsearchService.advancedSearch( + firstName, middleName, lastName, genderId, dob, stateId, districtId, + blockId, villageId, fatherName, spouseName, maritalStatus, phoneNumber, + beneficiaryId, healthId, aadharNo, userId + ); + + response.put("data", esResults); + response.put("count", esResults.size()); + response.put("source", "elasticsearch"); + + logger.info("ES returned {} results", esResults.size()); + + } else { + logger.info("ES disabled - using database for advanced search"); + + IdentitySearchDTO searchDTO = new IdentitySearchDTO(); + searchDTO.setFirstName(firstName); + searchDTO.setLastName(lastName); + searchDTO.setGenderId(genderId); + searchDTO.setDob(dob != null ? new Timestamp(dob.getTime()) : null); + searchDTO.setFatherName(fatherName); + searchDTO.setSpouseName(spouseName); + searchDTO.setContactNumber(phoneNumber); + + if (beneficiaryId != null && !beneficiaryId.trim().isEmpty()) { + try { + searchDTO.setBeneficiaryId(new BigInteger(beneficiaryId)); + } catch (NumberFormatException e) { + logger.warn("Invalid beneficiaryId format: {}", beneficiaryId); + } } + + if (stateId != null || districtId != null || blockId != null || villageId != null) { + Address addressDTO = new Address(); + addressDTO.setStateId(stateId); + addressDTO.setDistrictId(districtId); + addressDTO.setSubDistrictId(blockId); + addressDTO.setVillageId(villageId); + searchDTO.setCurrentAddress(addressDTO); + } + + List dbResults = this.getBeneficiaries(searchDTO); + + List> formattedResults = dbResults.stream() + .map(this::convertBeneficiaryDTOToMap) + .collect(Collectors.toList()); + + response.put("data", formattedResults); + response.put("count", formattedResults.size()); + response.put("source", "database"); + + logger.info("Database returned {} results", formattedResults.size()); } - - if (stateId != null || districtId != null || blockId != null || villageId != null) { - Address addressDTO = new Address(); - addressDTO.setStateId(stateId); - addressDTO.setDistrictId(districtId); - addressDTO.setSubDistrictId(blockId); - addressDTO.setVillageId(villageId); - searchDTO.setCurrentAddress(addressDTO); - } - - List dbResults = this.getBeneficiaries(searchDTO); - - List> formattedResults = dbResults.stream() - .map(this::convertBeneficiaryDTOToMap) - .collect(Collectors.toList()); - - response.put("data", formattedResults); - response.put("count", formattedResults.size()); - response.put("source", "database"); - - logger.info("Database returned {} results", formattedResults.size()); - } - - logger.info("IdentityService.advancedSearchBeneficiariesES - end"); - return response; - - } catch (Exception e) { - logger.error("Advanced search failed: {}", e.getMessage(), e); - throw new Exception("Error in advanced search: " + e.getMessage(), e); - } -}/** - * Convert BeneficiariesDTO to Map format - */ -private Map convertBeneficiaryDTOToMap(BeneficiariesDTO dto) { - try { - ObjectMapper mapper = new ObjectMapper(); - String json = mapper.writeValueAsString(dto); - return mapper.readValue(json, Map.class); - } catch (Exception e) { - logger.error("Error converting DTO to map", e); - return new HashMap<>(); + + logger.info("IdentityService.advancedSearchBeneficiariesES - end"); + return response; + + } catch (Exception e) { + logger.error("Advanced search failed: {}", e.getMessage(), e); + throw new Exception("Error in advanced search: " + e.getMessage(), e); + } + }/** + * Convert BeneficiariesDTO to Map format + */ + private Map convertBeneficiaryDTOToMap(BeneficiariesDTO dto) { + try { + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(dto); + return mapper.readValue(json, Map.class); + } catch (Exception e) { + logger.error("Error converting DTO to map", e); + return new HashMap<>(); + } } -} - /** + /** * * * * Search beneficiary by healthID / ABHA address @@ -763,7 +763,7 @@ public List searhBeneficiaryByFamilyId(String familyId) List benDetailsList = detailRepo.searchByFamilyId(familyId); if (benDetailsList == null || benDetailsList.isEmpty()) { - return beneficiaryList; + return beneficiaryList; }else { // considering as of now family creation is possible through facility modules // only @@ -789,7 +789,7 @@ public List searhBeneficiaryByFamilyId(String familyId) } public List searchBeneficiaryByVillageIdAndLastModifyDate(List villageIDs, - Timestamp lastModifiedDate) { + Timestamp lastModifiedDate) { List beneficiaryList = new ArrayList<>(); try { @@ -843,7 +843,7 @@ public List searhBeneficiaryByGovIdentity(String identity) // find benmap ids if (benIdentityList == null || benIdentityList.isEmpty()) { - return beneficiaryList; + return beneficiaryList; }else { for (MBeneficiaryidentity identityObj : benIdentityList) { benMapObjArr.addAll( @@ -1124,7 +1124,7 @@ public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFields benMapping.getMBeneficiaryaddress().getBenAddressID(), benMapping.getVanID()); // next statement is new one, setting correct beneficiaryDetailsId if (benAddressID != null) { - mbAddr.setBenAddressID(benAddressID); + mbAddr.setBenAddressID(benAddressID); }else { throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); } @@ -1151,7 +1151,7 @@ public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFields benMapping.getMBeneficiarycontact().getBenContactsID(), benMapping.getVanID()); // next statement is new one, setting correct beneficiaryDetailsId if (benContactsID != null) { - benCon.setBenContactsID(benContactsID); + benCon.setBenContactsID(benContactsID); }else { throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); } @@ -1269,7 +1269,7 @@ public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFields benMapping.getMBeneficiaryAccount().getBenAccountID(), benMapping.getVanID()); // next statement is new one, setting correct beneficiaryDetailsId if (benAccountID != null) { - beneficiaryAccount.setBenAccountID(benAccountID); + beneficiaryAccount.setBenAccountID(benAccountID); }else { throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); } @@ -1294,7 +1294,7 @@ public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFields benMapping.getMBeneficiaryImage().getBenImageId(), benMapping.getVanID()); // next statement is new one, setting correct beneficiaryDetailsId if (benImageId != null) { - beneficiaryImage.setBenImageId(benImageId); + beneficiaryImage.setBenImageId(benImageId); }else { throw new MissingMandatoryFieldsException("Either of vanSerialNO or vanID is missing."); } @@ -1314,11 +1314,11 @@ public void editIdentity(IdentityEditDTO identity) throws MissingMandatoryFields logger.info("Triggering Elasticsearch sync for benRegId: {}", identity.getBeneficiaryRegId()); syncService.syncBeneficiaryAsync(identity.getBeneficiaryRegId()); } - - logger.info("IdentityService.editIdentity - end. id = " + benMapping.getBenMapId()); -} - + logger.info("IdentityService.editIdentity - end. id = " + benMapping.getBenMapId()); + } + + private MBeneficiarydetail convertIdentityEditDTOToMBeneficiarydetail(IdentityEditDTO dto) { MBeneficiarydetail beneficiarydetail = new MBeneficiarydetail(); @@ -1829,6 +1829,12 @@ private MBeneficiaryaddress identityDTOToMBeneficiaryaddress(IdentityDTO dto) { beneficiaryAddress.setCurrServicePointId(dto.getCurrentAddress().getServicePointID()); beneficiaryAddress.setCurrServicePoint(dto.getCurrentAddress().getServicePointName()); beneficiaryAddress.setCurrHabitation(dto.getCurrentAddress().getHabitation()); + beneficiaryAddress.setGpsLatitude(dto.getCurrentAddress().getGpsLatitude()); + beneficiaryAddress.setGpsLongitude(dto.getCurrentAddress().getGpsLongitude()); + beneficiaryAddress.setDigipin(dto.getCurrentAddress().getDigipin()); + beneficiaryAddress.setGpsTimestamp(dto.getCurrentAddress().getGpsTimestamp()); + beneficiaryAddress.setIsGpsUnavailable(dto.getCurrentAddress().getIsGpsUnavailable()); + beneficiaryAddress.setGpsUnavailableReason(dto.getCurrentAddress().getGpsUnavailableReason()); } if (dto.getEmergencyAddress() != null) { beneficiaryAddress.setEmerAddrLine1(dto.getEmergencyAddress().getAddrLine1()); @@ -1939,7 +1945,7 @@ public String unReserveIdentity(ReserveIdentityDTO unReserve) { * Get partial details of beneficiaries (first name middle name and last * name) list on benId's list * - * @param BenRegIds + * @param benRegIds * @return {@link List} Beneficiaries */ public List getBeneficiariesPartialDeatilsByBenRegIdList(List benRegIds) { @@ -2157,7 +2163,7 @@ public int importBenIdToLocalServer(List benIdImportDTOList) { List dataList = new ArrayList<>(); Object[] objArr; String query = " INSERT INTO m_beneficiaryregidmapping(BenRegId, BeneficiaryID, " - + " Provisioned, CreatedDate, CreatedBy, Reserved) VALUES (?,?,?,?,?,?) "; + + " Provisioned, CreatedDate, CreatedBy, Reserved, vanID) VALUES (?,?,?,?,?,?,?) "; logger.info("query : " + query); for (MBeneficiaryregidmapping obj : mBeneficiaryregidmappingList) { logger.info("inside for check->", obj); 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 3bbd6b09..528310d4 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 @@ -46,6 +46,7 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.google.gson.Gson; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -73,6 +74,9 @@ import com.iemr.common.identity.repo.rmnch.RMNCHCBACDetailsRepo; import com.iemr.common.identity.repo.rmnch.RMNCHHouseHoldDetailsRepo; import com.iemr.common.identity.repo.rmnch.RMNCHMBenMappingRepo; +import com.iemr.common.identity.domain.MBeneficiarydetail; +import com.iemr.common.identity.repo.BenDetailRepo; +import com.iemr.common.identity.utils.redis.RedisStorage; import com.iemr.common.identity.repo.rmnch.RMNCHMBenRegIdMapRepo; import com.iemr.common.identity.utils.config.ConfigProperties; import com.iemr.common.identity.utils.exception.IEMRException; @@ -116,6 +120,16 @@ public class RmnchDataSyncServiceImpl implements RmnchDataSyncService { @Value("${fhir-url}") private String fhirUrl; + + @Autowired + private BenDetailRepo benDetailRepo; + @Autowired + private RedisStorage redisStorage; + + // When true, sync fails loudly if camp is not configured instead of silently + // skipping vanID stamping + @Value("${stoptb.enforce.vanid:false}") + private boolean enforceVanID; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) @Override public String syncDataToAmrit(String requestOBJ, String authorization) throws Exception { @@ -128,6 +142,24 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex ArrayList cBACDetailsIds = new ArrayList<>(); ArrayList houseHoldDetailsIds = new ArrayList<>(); + // Read camp vanID/parkingPlaceID from Redis (set by MMU-API on van login) + Integer campVanID = null; + Integer campParkingPlaceID = null; + try { + String vanVal = redisStorage.getRaw("camp:vanID"); + String ppVal = redisStorage.getRaw("camp:parkingPlaceID"); + if (vanVal != null && !vanVal.isBlank()) campVanID = Integer.parseInt(vanVal); + if (ppVal != null && !ppVal.isBlank()) campParkingPlaceID = Integer.parseInt(ppVal); + } catch (Exception ignored) { + // no camp configured — vanID stamping skipped + } + if (campVanID == null && enforceVanID) { + throw new Exception( + "Camp not configured: vanID missing. Please select van/service point in MMU before syncing data."); + } + final Integer vanID = campVanID; + final Integer parkingPlaceID = campParkingPlaceID; + try { if (requestOBJ != null && !requestOBJ.isEmpty()) { JsonObject jsnOBJ = new JsonObject(); @@ -151,10 +183,37 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex // benRegID = rMNCHMBenRegIdMapRepo.getRegID(benDetailsExtraList.get(0).getBenficieryid()); // // if (benRegID != null) { - + + // Build GPS lookup map from i_bendemographics in raw JSON + Map benGpsMap = new HashMap<>(); + JsonArray benJsonArr = jsnOBJ.getAsJsonArray("beneficiaryDetails"); + for (JsonElement el : benJsonArr) { + JsonObject benJson = el.getAsJsonObject(); + if (benJson.has("benficieryid") && !benJson.get("benficieryid").isJsonNull() + && benJson.has("i_bendemographics") + && !benJson.get("i_bendemographics").isJsonNull()) { + benGpsMap.put(benJson.get("benficieryid").getAsBigInteger(), + benJson.getAsJsonObject("i_bendemographics")); + } + } + for (RMNCHBeneficiaryDetailsRmnch obj : benDetailsExtraList) { benRegID = rMNCHMBenRegIdMapRepo.getRegID(obj.getBenficieryid()); obj.setBenRegId(benRegID); + // Extract GPS from i_bendemographics + JsonObject demog = benGpsMap.get(obj.getBenficieryid()); + if (demog != null) { + if (demog.has("latitude") && !demog.get("latitude").isJsonNull()) + obj.setGpsLatitude(demog.get("latitude").getAsDouble()); + if (demog.has("longitude") && !demog.get("longitude").isJsonNull()) + obj.setGpsLongitude(demog.get("longitude").getAsDouble()); + if (demog.has("digipin") && !demog.get("digipin").isJsonNull()) + obj.setDigipin(demog.get("digipin").getAsString()); + if (demog.has("gpsTimestamp") && !demog.get("gpsTimestamp").isJsonNull()) + obj.setGpsTimestamp(new Timestamp(demog.get("gpsTimestamp").getAsLong())); + if (demog.has("isGpsUnavailable") && !demog.get("isGpsUnavailable").isJsonNull()) + obj.setIsGpsUnavailable(demog.get("isGpsUnavailable").getAsBoolean()); + } if(!rMNCHBeneficiaryDetailsRmnchRepo .getByRegID(benRegID).isEmpty()){ RMNCHBeneficiaryDetailsRmnch temp = rMNCHBeneficiaryDetailsRmnchRepo @@ -178,6 +237,10 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex } obj.setRelatedBeneficiaryIdsDB(sb.toString()); } + if (obj.getVanID() == null && vanID != null) { + obj.setVanID(vanID); + obj.setParkingPlaceID(parkingPlaceID); + } if(!rMNCHBenDetailsRepo.getByBenRegID(obj.getBenRegId()).isEmpty()){ RMNCHMBeneficiarydetail rmnchmBeneficiarydetail = rMNCHBenDetailsRepo.getByBenRegID(obj.getBenRegId()).get(0); @@ -208,6 +271,9 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex } + // Keep original list before saveAll — @Transient fields (height/weight/bmi/temperature) + // are lost in the JPA-managed instances returned by merge() + List benDetailsOriginalList = new ArrayList<>(benDetailsExtraList); benDetailsExtraList = (ArrayList) rMNCHBeneficiaryDetailsRmnchRepo .saveAll(benDetailsExtraList); @@ -215,6 +281,22 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex // update beneficiary data in i_beneficiarydetails table rMNCHBenDetailsRepo.saveAll(benDetailsList); + // Write anthropometry (height/weight/bmi/temperature) to i_beneficiarydetails.otherFields. + // i_beneficiarydetails_rmnch has no these columns; FLW-API getBeneficiaryData reads from otherFields. + for (RMNCHBeneficiaryDetailsRmnch obj : benDetailsOriginalList) { + if (obj.getBenRegId() != null && hasAnthropometryData(obj)) { + try { + MBeneficiarydetail benDetail = benDetailRepo.findByBenRegId(obj.getBenRegId()); + if (benDetail != null) { + String merged = mergeAnthropometry(benDetail.getOtherFields(), obj); + benDetailRepo.updateOtherFieldsByBenRegId(obj.getBenRegId(), merged); + } + } catch (Exception ex) { + logger.warn("Failed to update otherFields for benRegId: " + obj.getBenRegId() + " - " + ex.getMessage()); + } + } + } + // born birth details if (jsnOBJ != null && jsnOBJ.has("bornBirthDeatils")) { RMNCHBornBirthDetails[] objArr1 = InputMapper.gson() @@ -228,7 +310,10 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex if (temp != null) obj.setBornBirthDeatilsId(temp.getBornBirthDeatilsId()); } - + if (obj.getVanID() == null && vanID != null) { + obj.setVanID(vanID); + obj.setParkingPlaceID(parkingPlaceID); + } } bornBirthList = (ArrayList) rMNCHBornBirthDetailsRepo .saveAll(bornBirthList); @@ -254,7 +339,10 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex if (temp != null) obj.setCBACDetailsid(temp.getCBACDetailsid()); } - + if (obj.getVanID() == null && vanID != null) { + obj.setVanID(vanID); + obj.setParkingPlaceID(parkingPlaceID); + } } cbacList = (ArrayList) rMNCHCBACDetailsRepo.saveAll(cbacList); @@ -267,6 +355,22 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex .fromJson(jsnOBJ.get("houseHoldDetails"), RMNCHHouseHoldDetails[].class); List houseHoldList = Arrays.asList(objArr3); + // Build gpsTimestamp map (sent as string, needs manual parse) + Map hhTimestampMap = new HashMap<>(); + JsonArray hhJsonArr = jsnOBJ.getAsJsonArray("houseHoldDetails"); + for (JsonElement el : hhJsonArr) { + JsonObject hhJson = el.getAsJsonObject(); + try { + if (hhJson.has("houseoldId") && !hhJson.get("houseoldId").isJsonNull() + && hhJson.has("gpsTimestamp") + && !hhJson.get("gpsTimestamp").isJsonNull()) { + hhTimestampMap.put( + Long.parseLong(hhJson.get("houseoldId").getAsString()), + hhJson.get("gpsTimestamp").getAsLong()); + } + } catch (NumberFormatException ignored) {} + } + for (RMNCHHouseHoldDetails obj : houseHoldList) { if(!rMNCHHouseHoldDetailsRepo .getByHouseHoldID(obj.getHouseoldId()).isEmpty()){ @@ -274,8 +378,13 @@ public String syncDataToAmrit(String requestOBJ, String authorization) throws Ex .getByHouseHoldID(obj.getHouseoldId()).get(0); if (temp != null) obj.setHouseHoldDetailsId(temp.getHouseHoldDetailsId()); + if (hhTimestampMap.containsKey(obj.getHouseoldId())) + obj.setGpsTimestamp(new Timestamp(hhTimestampMap.get(obj.getHouseoldId()))); + } + if (obj.getVanID() == null && vanID != null) { + obj.setVanID(vanID); + obj.setParkingPlaceID(parkingPlaceID); } - } houseHoldList = (ArrayList) rMNCHHouseHoldDetailsRepo .saveAll(houseHoldList); @@ -543,6 +652,28 @@ private Integer getInt(JsonObject obj, String key, Integer defaultVal) { ? obj.get(key).getAsInt() : defaultVal; } + + private boolean hasAnthropometryData(RMNCHBeneficiaryDetailsRmnch obj) { + return obj.getHeight() != null || obj.getWeight() != null + || obj.getBmi() != null || obj.getTemperature() != null; + } + + private String mergeAnthropometry(String existingOtherFields, RMNCHBeneficiaryDetailsRmnch obj) { + JsonObject json = new JsonObject(); + if (existingOtherFields != null && !existingOtherFields.isBlank()) { + try { + json = new JsonParser().parse(existingOtherFields).getAsJsonObject(); + } catch (Exception ignored) { + } + } + if (obj.getHeight() != null) json.addProperty("height", obj.getHeight()); + if (obj.getWeight() != null) json.addProperty("weight", obj.getWeight()); + if (obj.getBmi() != null) json.addProperty("bmi", obj.getBmi()); + // mobile sends "temperature"; FLW-API getBeneficiaryData reads "temperatureValue" + if (obj.getTemperature() != null) json.addProperty("temperatureValue", obj.getTemperature()); + return new Gson().toJson(json); + } + @Override public String getBenData(String requestOBJ, String authorisation) throws Exception { String outputResponse = null; @@ -789,6 +920,8 @@ private String getMappingsForAddressIDs(List addressLi benDetailsRMNCHOBJ.setAddressLine2(benAddressOBJ.getPermAddrLine2()); if (benAddressOBJ.getPermAddrLine3() != null) benDetailsRMNCHOBJ.setAddressLine3(benAddressOBJ.getPermAddrLine3()); + if (benAddressOBJ.getPermPinCode() != null) + benDetailsRMNCHOBJ.setPinCode(benAddressOBJ.getPermPinCode()); // related benids if (benDetailsRMNCHOBJ.getRelatedBeneficiaryIdsDB() != null) { diff --git a/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java b/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java index 4c8db37d..6dbc0301 100644 --- a/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java +++ b/src/main/java/com/iemr/common/identity/utils/mapper/InputMapper.java @@ -48,6 +48,11 @@ public InputMapper() { if (builder == null) { builder = new GsonBuilder(); builder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + // Timestamp fields (including dob) use Gson's default parsing here, same as + // on vb/stoptb. The gpsTimestamp field on RMNCH entities is parsed by + // com.iemr.common.identity.mapper.GpsTimestampAdapter via a field-level + // @JsonAdapter annotation instead of a global registration, so it can't + // affect any other Timestamp field. } } diff --git a/src/main/java/com/iemr/common/identity/utils/redis/RedisStorage.java b/src/main/java/com/iemr/common/identity/utils/redis/RedisStorage.java index 04a3f1d8..98ced6fa 100644 --- a/src/main/java/com/iemr/common/identity/utils/redis/RedisStorage.java +++ b/src/main/java/com/iemr/common/identity/utils/redis/RedisStorage.java @@ -67,6 +67,13 @@ public String getObject(String key, Boolean extendExpirationTime, int expiration return userRespFromRedis; } + public String getRaw(String key) { + RedisConnection redCon = connection.getConnection(); + byte[] data = redCon.get(key.getBytes()); + redCon.close(); + return data != null ? new String(data) : null; + } + public Long deleteObject(String key) throws RedisSessionException { RedisConnection redCon = connection.getConnection(); Long userRespFromRedis = Long.valueOf(0L);