Skip to content

Commit 26032ed

Browse files
vishwab1claudesehjotsinghunthinkable
authored
Merge 3.8.2 code (#177)
* feat: write anthropometry from rmnch/syncDataToAmrit to i_beneficiarydetails.otherFields Mobile sends height/weight/bmi/temperature in beneficiaryDetails payload. i_beneficiarydetails_rmnch has no these columns so they were lost. FLW-API getBeneficiaryData reads from otherFields (temperatureValue key). - RMNCHBeneficiaryDetailsRmnch: @transient height/weight/bmi/temperature - BenDetailRepo: updateOtherFieldsByBenRegId query - RmnchDataSyncServiceImpl: merge anthropometry into otherFields after save Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: stamp vanID from Redis on all 4 RMNCH entities in syncDataToAmrit Reads camp:vanID and camp:parkingPlaceID from Redis (written by MMU-API on van login) and stamps them onto RMNCHBeneficiaryDetailsRmnch, RMNCHBornBirthDetails, RMNCHCBACdetails, and RMNCHHouseHoldDetails when the mobile payload carries VanID=null or 0. Also writes anthropometry (height/weight/bmi/temperature) from mobile beneficiaryDetails payload into i_beneficiarydetails.otherFields so FLW-API getBeneficiaryData can return them. Gracefully skips if Redis has no camp configured. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: preserve @transient anthropometry fields lost after JPA saveAll merge Hibernate merge() returns new managed instances that do not carry @transient field values (height/weight/bmi/temperature). Keep a reference to the original list before saveAll so the anthropometry loop reads from objects that still have the mobile payload values. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: prevent camp:vanID deletion on every syncDataToAmrit call getObject("camp:vanID", false, 0) was calling EXPIRE key 0 which immediately deletes the key in Redis after every sync. Replaced with getRaw() which reads the value without modifying TTL, so camp:vanID persists across multiple syncs until MMU logout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(stoptb): add stoptb.enforce.vanid flag to syncDataToAmrit The Stop TB mobile app calls this RMNCH sync endpoint directly to save household details. Same issue as elsewhere: vanID stamping is skipped silently when Redis has no camp:vanID, so household records end up with vanID=NULL. When stoptb.enforce.vanid=true, sync now fails with a clear error instead. * feat(STOP-148): add GPS location capture support for beneficiary and RMNCH records Introduces GPS-related fields (gpsLatitude, gpsLongitude, digipin, gpsTimestamp, isGpsUnavailable, and gpsUnavailableReason) across beneficiary address and RMNCH domain models, including MBeneficiaryaddress, Address DTO, RMNCHBeneficiaryDetailsRmnch, and RMNCHHouseHoldDetails. Enhances IdentityMapper and IdentityService to map and persist GPS information between incoming DTOs and beneficiary address entities. Updates RmnchDataSyncServiceImpl to extract and synchronize GPS details from the nested i_bendemographics payload during beneficiary sync, and to parse gpsTimestamp from household details during RMNCH household data processing. * feat(STOP-148): add GPS location capture support for beneficiary and RMNCH records (#167) Introduces GPS-related fields (gpsLatitude, gpsLongitude, digipin, gpsTimestamp, isGpsUnavailable, and gpsUnavailableReason) across beneficiary address and RMNCH domain models, including MBeneficiaryaddress, Address DTO, RMNCHBeneficiaryDetailsRmnch, and RMNCHHouseHoldDetails. Enhances IdentityMapper and IdentityService to map and persist GPS information between incoming DTOs and beneficiary address entities. Updates RmnchDataSyncServiceImpl to extract and synchronize GPS details from the nested i_bendemographics payload during beneficiary sync, and to parse gpsTimestamp from household details during RMNCH household data processing. Co-authored-by: Sehjot Singh Pannu <sehjot.singh@unthinkable.co> * fix(STOP-148): use InputMapper Gson for createIdentity to parse epoch-millis gpsTimestamp createIdentity() parsed the request body with a bare new Gson(), whose default Timestamp adapter only accepts ISO8601 date strings. The new gpsTimestamp field is sent as raw epoch millis, causing JsonSyntaxException during parsing whenever GPS data is present. That exception propagated up through JwtUserIdValidationFilter's catch block and was misreported as a 401 Authorization error. InputMapper already registers a Timestamp adapter that handles epoch millis correctly; switch createIdentity() to use it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(datasync): add vanID column to BenGenID import INSERT query SQL had 6 placeholders but object array had 7 elements including vanID, causing parameter index out of bounds error. Added vanID to INSERT columns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(rmnch): surface pinCode in beneficiary details sync response permPinCode was already stored correctly in i_beneficiaryaddress but was never surfaced in the RMNCH sync response, since the response DTO had no pinCode field and the address mapping never copied it across. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(dob): stop global Timestamp adapter from nulling dob on GPS-enabled endpoints The GPS feature (STOP-148) registered a custom Gson TypeAdapter<Timestamp> globally on InputMapper's GsonBuilder to parse the new gpsTimestamp field. Because it was global, it also intercepted dob, silently returning null whenever the incoming date string didn't match one of its four hardcoded formats. - Add GpsTimestampAdapter, attached only via @JsonAdapter on the gpsTimestamp field (Address, RMNCHBeneficiaryDetailsRmnch, RMNCHHouseHoldDetails), so it can't affect any other Timestamp field. - Remove the global registerTypeAdapter(Timestamp.class, ...) from both InputMapper.java copies, restoring Gson's default Timestamp parsing for dob and everything else (matching vb/stoptb). - Revert createIdentity()'s parser back to a bare new Gson(), matching Common-API's RegisterBenificiaryServiceImpl, which also serializes the outgoing identity payload with a bare new Gson(). InputMapper's setDateFormat is incompatible with that wire format (non-zero-padded day), which is why dob was nulling specifically for beneficiaries born on the 1st-9th of a month once createIdentity switched to InputMapper's Gson. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Sehjot Singh Pannu <sehjot.singh@unthinkable.co>
1 parent 5696472 commit 26032ed

16 files changed

Lines changed: 618 additions & 214 deletions

src/main/environment/common_ci.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ fhir-url=@env.FHIR_API@
2222
# Redis Config
2323
spring.redis.host=@env.REDIS_HOST@
2424

25+
# Stop TB: when true, RMNCH data sync fails with an error if camp (vanID) is not configured
26+
stoptb.enforce.vanid=@env.STOPTB_ENFORCE_VANID@
27+
2528
cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@
2629

2730
# Elasticsearch Configuration

src/main/environment/common_docker.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ fhir-url=${FHIR_API}
2222
# Redis Config
2323
spring.redis.host=${REDIS_HOST}
2424

25+
# Stop TB: when true, RMNCH data sync fails with an error if camp (vanID) is not configured
26+
stoptb.enforce.vanid=${STOPTB_ENFORCE_VANID}
27+
2528
cors.allowed-origins=${CORS_ALLOWED_ORIGINS}
2629

2730
# Elasticsearch Configuration

src/main/environment/common_example.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ fhir-url=http://localhost:8093/
1717

1818
# Redis Config
1919
spring.redis.host=localhost
20+
21+
# Stop TB: when true, RMNCH data sync fails with an error if camp (vanID) is not
22+
# configured instead of silently skipping vanID stamping
23+
stoptb.enforce.vanid=false
24+
2025
cors.allowed-origins=http://localhost:*
2126

2227
# Elasticsearch Configuration

src/main/java/com/iemr/common/identity/controller/IdentityController.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,11 @@ public String createIdentity(@Param(value = "{\r\n" + " \"eventTypeName\": \"St
608608
+ " \"createdDate\": \"Timestamp\"\r\n" + " \"faceEmbedding\": [\"Float\"]\r\n" + "}") @RequestBody String identityData) throws IEMRException {
609609
logger.info("IdentityController.createIdentity - start");
610610

611+
// Bare Gson matches Common-API's RegisterBenificiaryServiceImpl, which also
612+
// serializes the outgoing identity payload with a bare new Gson(). dob relies
613+
// on this symmetric default format; gpsTimestamp is still parsed correctly via
614+
// its field-level @JsonAdapter(GpsTimestampAdapter.class) on Address, which
615+
// works regardless of which Gson instance performs the parse.
611616
IdentityDTO identity = new Gson().fromJson(identityData, IdentityDTO.class);
612617
logger.info("identity hit: " + identity);
613618
BeneficiaryCreateResp map;

src/main/java/com/iemr/common/identity/data/rmnch/RMNCHBeneficiaryDetailsRmnch.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import jakarta.persistence.Transient;
3535

3636
import com.google.gson.annotations.Expose;
37+
import com.google.gson.annotations.JsonAdapter;
38+
import com.iemr.common.identity.mapper.GpsTimestampAdapter;
3739

3840
import lombok.Data;
3941

@@ -390,6 +392,9 @@ public class RMNCHBeneficiaryDetailsRmnch {
390392
@Expose
391393
@Transient
392394
private String addressLine3;
395+
@Expose
396+
@Transient
397+
private String pinCode;
393398

394399
// ----------------------------------------------
395400

@@ -557,4 +562,45 @@ public class RMNCHBeneficiaryDetailsRmnch {
557562
@Expose
558563
@Transient
559564
private String familyId;
565+
566+
// Anthropometry fields sent by Stop TB mobile app via beneficiaryDetails payload.
567+
// i_beneficiarydetails_rmnch has no these columns — stored in i_beneficiarydetails.otherFields instead.
568+
@Expose
569+
@Transient
570+
private Double height;
571+
@Expose
572+
@Transient
573+
private Double weight;
574+
@Expose
575+
@Transient
576+
private Double bmi;
577+
@Expose
578+
@Transient
579+
private Double temperature; // stored as "temperatureValue" in otherFields to match getBeneficiaryData key
580+
581+
@Expose
582+
@Column(name = "gpsLatitude")
583+
private Double gpsLatitude;
584+
585+
@Expose
586+
@Column(name = "gpsLongitude")
587+
private Double gpsLongitude;
588+
589+
@Expose
590+
@Column(name = "digipin")
591+
private String digipin;
592+
593+
@Expose
594+
@Column(name = "gpsTimestamp")
595+
@JsonAdapter(GpsTimestampAdapter.class)
596+
private Timestamp gpsTimestamp;
597+
598+
@Expose
599+
@Column(name = "isGpsUnavailable", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0")
600+
private Boolean isGpsUnavailable = false;
601+
602+
@Expose
603+
@Column(name = "gpsUnavailableReason")
604+
private String gpsUnavailableReason;
605+
560606
}

src/main/java/com/iemr/common/identity/data/rmnch/RMNCHHouseHoldDetails.java

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
/*
2-
* AMRIT – Accessible Medical Records via Integrated Technology
3-
* Integrated EHR (Electronic Health Records) Solution
4-
*
5-
* Copyright (C) "Piramal Swasthya Management and Research Institute"
6-
*
7-
* This file is part of AMRIT.
8-
*
9-
* This program is free software: you can redistribute it and/or modify
10-
* it under the terms of the GNU General Public License as published by
11-
* the Free Software Foundation, either version 3 of the License, or
12-
* (at your option) any later version.
13-
*
14-
* This program is distributed in the hope that it will be useful,
15-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17-
* GNU General Public License for more details.
18-
*
19-
* You should have received a copy of the GNU General Public License
20-
* along with this program. If not, see https://www.gnu.org/licenses/.
21-
*/
2+
* AMRIT – Accessible Medical Records via Integrated Technology
3+
* Integrated EHR (Electronic Health Records) Solution
4+
*
5+
* Copyright (C) "Piramal Swasthya Management and Research Institute"
6+
*
7+
* This file is part of AMRIT.
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program. If not, see https://www.gnu.org/licenses/.
21+
*/
2222
package com.iemr.common.identity.data.rmnch;
2323

2424
import java.sql.Timestamp;
@@ -31,11 +31,14 @@
3131
import jakarta.persistence.Table;
3232

3333
import com.google.gson.annotations.Expose;
34+
import com.google.gson.annotations.JsonAdapter;
35+
import com.google.gson.annotations.SerializedName;
36+
import com.iemr.common.identity.mapper.GpsTimestampAdapter;
3437

3538
import lombok.Data;
3639

3740
/**
38-
*
41+
*
3942
* @author de40034072
4043
*
4144
*/
@@ -359,4 +362,31 @@ public class RMNCHHouseHoldDetails {
359362
@Column(name = "mohallaName")
360363
private String mohallaName;
361364

365+
@Expose
366+
@SerializedName("latitude")
367+
@Column(name = "gpsLatitude")
368+
private Double gpsLatitude;
369+
370+
@Expose
371+
@SerializedName("longitude")
372+
@Column(name = "gpsLongitude")
373+
private Double gpsLongitude;
374+
375+
@Expose
376+
@Column(name = "digipin")
377+
private String digipin;
378+
379+
@Expose
380+
@Column(name = "gpsTimestamp")
381+
@JsonAdapter(GpsTimestampAdapter.class)
382+
private Timestamp gpsTimestamp;
383+
384+
@Expose
385+
@Column(name = "isGpsUnavailable", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0")
386+
private Boolean isGpsUnavailable = false;
387+
388+
@Expose
389+
@Column(name = "gpsUnavailableReason")
390+
private String gpsUnavailableReason;
391+
362392
}

src/main/java/com/iemr/common/identity/domain/Address.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323

2424
import lombok.Data;
2525

26+
import java.sql.Timestamp;
27+
28+
import com.google.gson.annotations.JsonAdapter;
29+
import com.iemr.common.identity.mapper.GpsTimestampAdapter;
30+
2631
public @Data class Address {
2732
private String addrLine1;
2833
private String addrLine2;
@@ -51,4 +56,11 @@
5156

5257
private Integer vanID;
5358
private Integer parkingPlaceID;
59+
private Double gpsLatitude;
60+
private Double gpsLongitude;
61+
private String digipin;
62+
@JsonAdapter(GpsTimestampAdapter.class)
63+
private Timestamp gpsTimestamp;
64+
private Boolean isGpsUnavailable;
65+
private String gpsUnavailableReason;
5466
}

src/main/java/com/iemr/common/identity/domain/MBeneficiaryaddress.java

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
/*
2-
* AMRIT – Accessible Medical Records via Integrated Technology
3-
* Integrated EHR (Electronic Health Records) Solution
4-
*
5-
* Copyright (C) "Piramal Swasthya Management and Research Institute"
6-
*
7-
* This file is part of AMRIT.
8-
*
9-
* This program is free software: you can redistribute it and/or modify
10-
* it under the terms of the GNU General Public License as published by
11-
* the Free Software Foundation, either version 3 of the License, or
12-
* (at your option) any later version.
13-
*
14-
* This program is distributed in the hope that it will be useful,
15-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17-
* GNU General Public License for more details.
18-
*
19-
* You should have received a copy of the GNU General Public License
20-
* along with this program. If not, see https://www.gnu.org/licenses/.
21-
*/
2+
* AMRIT – Accessible Medical Records via Integrated Technology
3+
* Integrated EHR (Electronic Health Records) Solution
4+
*
5+
* Copyright (C) "Piramal Swasthya Management and Research Institute"
6+
*
7+
* This file is part of AMRIT.
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program. If not, see https://www.gnu.org/licenses/.
21+
*/
2222
package com.iemr.common.identity.domain;
2323

2424
import java.io.Serializable;
@@ -39,7 +39,7 @@
3939

4040
/**
4141
* The persistent class for the m_beneficiaryaddress database table.
42-
*
42+
*
4343
*/
4444
@Entity
4545
@Table(name = "i_beneficiaryaddress")
@@ -260,6 +260,24 @@ public class MBeneficiaryaddress implements Serializable {
260260

261261
// END OF new column added for data sync
262262

263+
@Column(name = "gpsLatitude")
264+
private Double gpsLatitude;
265+
266+
@Column(name = "gpsLongitude")
267+
private Double gpsLongitude;
268+
269+
@Column(name = "digipin")
270+
private String digipin;
271+
272+
@Column(name = "gpsTimestamp")
273+
private Timestamp gpsTimestamp;
274+
275+
@Column(name = "isGpsUnavailable", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0")
276+
private Boolean isGpsUnavailable = false;
277+
278+
@Column(name = "gpsUnavailableReason")
279+
private String gpsUnavailableReason;
280+
263281
public MBeneficiaryaddress setCurrentAddress(Address address) {
264282
this.currAddrLine1 = address.getAddrLine1();
265283
this.currAddrLine2 = address.getAddrLine2();

0 commit comments

Comments
 (0)