From 608fce6a128c5d6cbd3d9fa9f14c91e09b1cbe5d Mon Sep 17 00:00:00 2001 From: vaibhav45sktech <232676907+vaibhav45sktech@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:12:22 +0530 Subject: [PATCH 01/24] feat(badges): config, freezes and earned-log endpoints for SAKHI badges GET /badges/{config,freezes,earned} and POST /badges/earned; the ASHA is taken from the JWT, re-uploads are idempotent, defaults seeded on boot. --- .../iemr/flw/controller/BadgeController.java | 108 ++++++++++++++++++ .../com/iemr/flw/domain/iemr/BadgeConfig.java | 28 +++++ .../com/iemr/flw/domain/iemr/BadgeEarned.java | 45 ++++++++ .../flw/domain/iemr/BadgeStreakFreeze.java | 37 ++++++ .../com/iemr/flw/dto/iemr/BadgeEarnedDTO.java | 15 +++ .../iemr/flw/dto/iemr/BadgeEarnedPushDTO.java | 15 +++ .../iemr/flw/repo/iemr/BadgeConfigRepo.java | 9 ++ .../iemr/flw/repo/iemr/BadgeEarnedRepo.java | 14 +++ .../flw/repo/iemr/BadgeStreakFreezeRepo.java | 13 +++ .../iemr/flw/seeder/BadgeConfigSeeder.java | 49 ++++++++ .../com/iemr/flw/service/BadgeService.java | 74 ++++++++++++ .../flw/controller/BadgeControllerTest.java | 89 +++++++++++++++ .../iemr/flw/service/BadgeServiceTest.java | 55 +++++++++ 13 files changed, 551 insertions(+) create mode 100644 src/main/java/com/iemr/flw/controller/BadgeController.java create mode 100644 src/main/java/com/iemr/flw/domain/iemr/BadgeConfig.java create mode 100644 src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java create mode 100644 src/main/java/com/iemr/flw/domain/iemr/BadgeStreakFreeze.java create mode 100644 src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java create mode 100644 src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedPushDTO.java create mode 100644 src/main/java/com/iemr/flw/repo/iemr/BadgeConfigRepo.java create mode 100644 src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java create mode 100644 src/main/java/com/iemr/flw/repo/iemr/BadgeStreakFreezeRepo.java create mode 100644 src/main/java/com/iemr/flw/seeder/BadgeConfigSeeder.java create mode 100644 src/main/java/com/iemr/flw/service/BadgeService.java create mode 100644 src/test/java/com/iemr/flw/controller/BadgeControllerTest.java create mode 100644 src/test/java/com/iemr/flw/service/BadgeServiceTest.java diff --git a/src/main/java/com/iemr/flw/controller/BadgeController.java b/src/main/java/com/iemr/flw/controller/BadgeController.java new file mode 100644 index 000000000..aa5fb4855 --- /dev/null +++ b/src/main/java/com/iemr/flw/controller/BadgeController.java @@ -0,0 +1,108 @@ +package com.iemr.flw.controller; + +import com.iemr.flw.dto.iemr.BadgeEarnedPushDTO; +import com.iemr.flw.service.BadgeService; +import com.iemr.flw.utils.JwtUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +/** + * Badges API consumed by the SAKHI app's BadgeSyncWorker: + * GET /badges/config -> { "config": { key: value } } + * GET /badges/freezes -> { "freezes": [ { badgeId, startDate, endDate } ] } + * GET /badges/earned -> { "earned": [ { badgeId, level, earnedAt } ] } + * POST /badges/earned <- { userId, badges: [ { badgeId, level, earnedAt } ] } + * The ASHA is always identified from the JWT, never from the payload. + */ +@RestController +@RequestMapping(value = "/badges", produces = "application/json") +public class BadgeController { + + private final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName()); + private final BadgeService badgeService; + private final JwtUtil jwtUtil; + + public BadgeController(BadgeService badgeService, JwtUtil jwtUtil) { + this.badgeService = badgeService; + this.jwtUtil = jwtUtil; + } + + @GetMapping("/config") + public ResponseEntity> getConfig() { + Map response = ok(); + try { + response.put("config", badgeService.getConfig()); + return ResponseEntity.ok(response); + } catch (Exception e) { + return error(e); + } + } + + @GetMapping("/freezes") + public ResponseEntity> getFreezes( + @RequestHeader(value = "JwtToken") String jwtToken) { + Map response = ok(); + try { + Integer userId = jwtUtil.extractUserId(jwtToken); + response.put("freezes", badgeService.getFreezes(userId)); + return ResponseEntity.ok(response); + } catch (Exception e) { + return error(e); + } + } + + @GetMapping("/earned") + public ResponseEntity> getEarned( + @RequestHeader(value = "JwtToken") String jwtToken) { + Map response = ok(); + try { + Integer userId = jwtUtil.extractUserId(jwtToken); + response.put("earned", badgeService.getEarned(userId)); + return ResponseEntity.ok(response); + } catch (Exception e) { + return error(e); + } + } + + @PostMapping(value = "/earned", consumes = "application/json") + public ResponseEntity> postEarned( + @RequestHeader(value = "JwtToken") String jwtToken, + @RequestBody BadgeEarnedPushDTO body) { + Map response = ok(); + try { + Integer userId = jwtUtil.extractUserId(jwtToken); + int inserted = badgeService.saveEarned(userId, body == null ? null : body.getBadges()); + response.put("inserted", inserted); + return ResponseEntity.ok(response); + } catch (Exception e) { + return error(e); + } + } + + private Map ok() { + Map response = new HashMap<>(); + response.put("statusCode", 200); + response.put("status", "Success"); + return response; + } + + private ResponseEntity> error(Exception e) { + logger.error("Badges API error:", e); + Map response = new HashMap<>(); + response.put("statusCode", 500); + response.put("status", "Error"); + response.put("errorMessage", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } +} diff --git a/src/main/java/com/iemr/flw/domain/iemr/BadgeConfig.java b/src/main/java/com/iemr/flw/domain/iemr/BadgeConfig.java new file mode 100644 index 000000000..909871114 --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/BadgeConfig.java @@ -0,0 +1,28 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Badge tuning as key/value rows (Badge LLD §5.2). Overrides the app's + * compiled defaults without a release, e.g. "milestones.steady_syncer" -> "2,4,6,8". + */ +@Entity +@Data +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "badge_config", schema = "db_iemr") +public class BadgeConfig { + + @Id + @Column(name = "config_key", length = 100) + private String key; + + @Column(name = "config_value", length = 500, nullable = false) + private String value; +} diff --git a/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java b/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java new file mode 100644 index 000000000..b73864257 --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java @@ -0,0 +1,45 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import lombok.Data; + +/** + * Append-only badge award log (Badge LLD §4.1, "once earned, never revoked"). + * The unique key mirrors the device's local constraint so re-uploads after a + * reinstall or a retried sync are idempotent. + */ +@Entity +@Data +@Table(name = "badge_earned", schema = "db_iemr", + uniqueConstraints = @UniqueConstraint( + name = "uk_badge_earned_user_badge_level", + columnNames = {"user_id", "badge_id", "level"})) +public class BadgeEarned { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id", nullable = false) + private Integer userId; + + @Column(name = "badge_id", length = 50, nullable = false) + private String badgeId; + + @Column(name = "level", nullable = false) + private Integer level; + + /** Epoch millis when the device awarded it. */ + @Column(name = "earned_at", nullable = false) + private Long earnedAt; + + /** Epoch millis when the server first received it. */ + @Column(name = "received_at", nullable = false) + private Long receivedAt; +} diff --git a/src/main/java/com/iemr/flw/domain/iemr/BadgeStreakFreeze.java b/src/main/java/com/iemr/flw/domain/iemr/BadgeStreakFreeze.java new file mode 100644 index 000000000..afc7a9870 --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/BadgeStreakFreeze.java @@ -0,0 +1,37 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +/** + * Streak-freeze window (Badge LLD §3.3): illness / alternate duty. A null userId + * applies to every ASHA; a null or empty badgeId applies to every streak badge. + */ +@Entity +@Data +@Table(name = "badge_streak_freeze", schema = "db_iemr") +public class BadgeStreakFreeze { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id") + private Integer userId; + + @Column(name = "badge_id", length = 50) + private String badgeId; + + /** Epoch millis, inclusive. */ + @Column(name = "start_date", nullable = false) + private Long startDate; + + /** Epoch millis, inclusive. */ + @Column(name = "end_date", nullable = false) + private Long endDate; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java b/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java new file mode 100644 index 000000000..9ce8d6a79 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java @@ -0,0 +1,15 @@ +package com.iemr.flw.dto.iemr; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** Wire shape shared with the app's BadgeEarnedDTO (Moshi). */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BadgeEarnedDTO { + private String badgeId; + private Integer level; + private Long earnedAt; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedPushDTO.java b/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedPushDTO.java new file mode 100644 index 000000000..dddca01c1 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedPushDTO.java @@ -0,0 +1,15 @@ +package com.iemr.flw.dto.iemr; + +import lombok.Data; + +import java.util.List; + +/** + * POST /badges/earned body. The userId field is accepted for wire compatibility + * but never trusted: the owner is always taken from the JWT. + */ +@Data +public class BadgeEarnedPushDTO { + private Integer userId; + private List badges; +} diff --git a/src/main/java/com/iemr/flw/repo/iemr/BadgeConfigRepo.java b/src/main/java/com/iemr/flw/repo/iemr/BadgeConfigRepo.java new file mode 100644 index 000000000..0341fa8f1 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/BadgeConfigRepo.java @@ -0,0 +1,9 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.BadgeConfig; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BadgeConfigRepo extends JpaRepository { +} diff --git a/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java b/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java new file mode 100644 index 000000000..219571a55 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java @@ -0,0 +1,14 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.BadgeEarned; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface BadgeEarnedRepo extends JpaRepository { + List findByUserIdOrderByEarnedAtAsc(Integer userId); + + boolean existsByUserIdAndBadgeIdAndLevel(Integer userId, String badgeId, Integer level); +} diff --git a/src/main/java/com/iemr/flw/repo/iemr/BadgeStreakFreezeRepo.java b/src/main/java/com/iemr/flw/repo/iemr/BadgeStreakFreezeRepo.java new file mode 100644 index 000000000..adc063d94 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/BadgeStreakFreezeRepo.java @@ -0,0 +1,13 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.BadgeStreakFreeze; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface BadgeStreakFreezeRepo extends JpaRepository { + /** Global windows (user_id IS NULL) plus this ASHA's own. */ + List findByUserIdIsNullOrUserId(Integer userId); +} diff --git a/src/main/java/com/iemr/flw/seeder/BadgeConfigSeeder.java b/src/main/java/com/iemr/flw/seeder/BadgeConfigSeeder.java new file mode 100644 index 000000000..1c83833a5 --- /dev/null +++ b/src/main/java/com/iemr/flw/seeder/BadgeConfigSeeder.java @@ -0,0 +1,49 @@ +package com.iemr.flw.seeder; + +import com.iemr.flw.domain.iemr.BadgeConfig; +import com.iemr.flw.repo.iemr.BadgeConfigRepo; +import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * Seeds the default badge tuning once (table empty). Values mirror the app's + * compiled defaults so a fresh server changes nothing until someone edits a row. + */ +@Component +public class BadgeConfigSeeder { + + private static final Logger log = LoggerFactory.getLogger(BadgeConfigSeeder.class); + + private final BadgeConfigRepo repo; + + public BadgeConfigSeeder(BadgeConfigRepo repo) { + this.repo = repo; + } + + @PostConstruct + public void seed() { + try { + if (repo.count() > 0) return; + repo.saveAll(List.of( + new BadgeConfig("feature_enabled", "true"), + new BadgeConfig("copy_version", "1"), + new BadgeConfig("milestones.steady_syncer", "2,4,6,8"), + new BadgeConfig("grace.steady_syncer", "1"), + new BadgeConfig("milestones.timely_reporter", "1,3,6,12"), + new BadgeConfig("milestones.complete_worker", "3"), + new BadgeConfig("milestones.community_voice", "2"), + new BadgeConfig("milestones.maternal_journey", "1,5,15,30"), + new BadgeConfig("milestones.child_fully_protected", "3,10,25,60"), + new BadgeConfig("milestones.digital_identity", "10,25,75,150"), + new BadgeConfig("enabled.critical_referral", "false") + )); + log.info("Badge config defaults seeded"); + } catch (Exception e) { + log.warn("Badge config seeding skipped: {}", e.getMessage()); + } + } +} diff --git a/src/main/java/com/iemr/flw/service/BadgeService.java b/src/main/java/com/iemr/flw/service/BadgeService.java new file mode 100644 index 000000000..074ad878e --- /dev/null +++ b/src/main/java/com/iemr/flw/service/BadgeService.java @@ -0,0 +1,74 @@ +package com.iemr.flw.service; + +import com.iemr.flw.domain.iemr.BadgeConfig; +import com.iemr.flw.domain.iemr.BadgeEarned; +import com.iemr.flw.domain.iemr.BadgeStreakFreeze; +import com.iemr.flw.dto.iemr.BadgeEarnedDTO; +import com.iemr.flw.repo.iemr.BadgeConfigRepo; +import com.iemr.flw.repo.iemr.BadgeEarnedRepo; +import com.iemr.flw.repo.iemr.BadgeStreakFreezeRepo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Badges backend (Badge LLD §4). The device owns evaluation; the server only + * distributes tuning + freeze windows and keeps the durable award log so a + * reinstall restores progress. + */ +@Service +public class BadgeService { + + private final BadgeConfigRepo configRepo; + private final BadgeStreakFreezeRepo freezeRepo; + private final BadgeEarnedRepo earnedRepo; + + public BadgeService(BadgeConfigRepo configRepo, BadgeStreakFreezeRepo freezeRepo, + BadgeEarnedRepo earnedRepo) { + this.configRepo = configRepo; + this.freezeRepo = freezeRepo; + this.earnedRepo = earnedRepo; + } + + public Map getConfig() { + Map out = new LinkedHashMap<>(); + for (BadgeConfig row : configRepo.findAll()) { + out.put(row.getKey(), row.getValue()); + } + return out; + } + + public List getFreezes(Integer userId) { + return freezeRepo.findByUserIdIsNullOrUserId(userId); + } + + public List getEarned(Integer userId) { + return earnedRepo.findByUserIdOrderByEarnedAtAsc(userId).stream() + .map(e -> new BadgeEarnedDTO(e.getBadgeId(), e.getLevel(), e.getEarnedAt())) + .toList(); + } + + /** Idempotent: rows already known for (user, badge, level) are skipped. Returns rows inserted. */ + @Transactional + public int saveEarned(Integer userId, List badges) { + if (badges == null || badges.isEmpty()) return 0; + long now = System.currentTimeMillis(); + int inserted = 0; + for (BadgeEarnedDTO dto : badges) { + if (dto == null || dto.getBadgeId() == null || dto.getLevel() == null) continue; + if (earnedRepo.existsByUserIdAndBadgeIdAndLevel(userId, dto.getBadgeId(), dto.getLevel())) continue; + BadgeEarned row = new BadgeEarned(); + row.setUserId(userId); + row.setBadgeId(dto.getBadgeId()); + row.setLevel(dto.getLevel()); + row.setEarnedAt(dto.getEarnedAt() != null ? dto.getEarnedAt() : now); + row.setReceivedAt(now); + earnedRepo.save(row); + inserted++; + } + return inserted; + } +} diff --git a/src/test/java/com/iemr/flw/controller/BadgeControllerTest.java b/src/test/java/com/iemr/flw/controller/BadgeControllerTest.java new file mode 100644 index 000000000..bb6069936 --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/BadgeControllerTest.java @@ -0,0 +1,89 @@ +package com.iemr.flw.controller; + +import com.iemr.flw.dto.iemr.BadgeEarnedDTO; +import com.iemr.flw.service.BadgeService; +import com.iemr.flw.utils.JwtUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class BadgeControllerTest { + + private MockMvc mockMvc; + + @Mock + private BadgeService badgeService; + + @Mock + private JwtUtil jwtUtil; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(new BadgeController(badgeService, jwtUtil)).build(); + } + + @Test + void config_returnsKeyValueMapUnderConfig() throws Exception { + when(badgeService.getConfig()).thenReturn(Map.of("milestones.steady_syncer", "2,4,6,8")); + + mockMvc.perform(get("/badges/config")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.config['milestones.steady_syncer']").value("2,4,6,8")); + } + + @Test + void earned_usesUserIdFromJwtNotFromBody() throws Exception { + when(jwtUtil.extractUserId("tok")).thenReturn(960); + when(badgeService.saveEarned(eq(960), any())).thenReturn(1); + + mockMvc.perform(post("/badges/earned") + .header("JwtToken", "tok") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"userId\":12345,\"badges\":[{\"badgeId\":\"steady_syncer\",\"level\":2,\"earnedAt\":1}]}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.inserted").value(1)); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(badgeService).saveEarned(eq(960), captor.capture()); + assertEquals("steady_syncer", captor.getValue().get(0).getBadgeId()); + } + + @Test + void getEarned_returnsListUnderEarned() throws Exception { + when(jwtUtil.extractUserId("tok")).thenReturn(960); + when(badgeService.getEarned(960)).thenReturn(List.of(new BadgeEarnedDTO("timely_reporter", 1, 5L))); + + mockMvc.perform(get("/badges/earned").header("JwtToken", "tok")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.earned[0].badgeId").value("timely_reporter")) + .andExpect(jsonPath("$.earned[0].level").value(1)); + } + + @Test + void invalidJwt_returns500WithErrorShape() throws Exception { + when(jwtUtil.extractUserId("bad")).thenThrow(new RuntimeException("Invalid JWT token.")); + + mockMvc.perform(get("/badges/earned").header("JwtToken", "bad")) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.status").value("Error")); + } +} diff --git a/src/test/java/com/iemr/flw/service/BadgeServiceTest.java b/src/test/java/com/iemr/flw/service/BadgeServiceTest.java new file mode 100644 index 000000000..b1e7ac384 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/BadgeServiceTest.java @@ -0,0 +1,55 @@ +package com.iemr.flw.service; + +import com.iemr.flw.domain.iemr.BadgeEarned; +import com.iemr.flw.dto.iemr.BadgeEarnedDTO; +import com.iemr.flw.repo.iemr.BadgeConfigRepo; +import com.iemr.flw.repo.iemr.BadgeEarnedRepo; +import com.iemr.flw.repo.iemr.BadgeStreakFreezeRepo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class BadgeServiceTest { + + @Mock private BadgeConfigRepo configRepo; + @Mock private BadgeStreakFreezeRepo freezeRepo; + @Mock private BadgeEarnedRepo earnedRepo; + + private BadgeService service; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + service = new BadgeService(configRepo, freezeRepo, earnedRepo); + } + + @Test + void saveEarned_skipsRowsAlreadyKnown_idempotentReupload() { + when(earnedRepo.existsByUserIdAndBadgeIdAndLevel(960, "steady_syncer", 1)).thenReturn(true); + when(earnedRepo.existsByUserIdAndBadgeIdAndLevel(960, "steady_syncer", 2)).thenReturn(false); + + int inserted = service.saveEarned(960, List.of( + new BadgeEarnedDTO("steady_syncer", 1, 10L), + new BadgeEarnedDTO("steady_syncer", 2, 20L))); + + assertEquals(1, inserted); + verify(earnedRepo, times(1)).save(any(BadgeEarned.class)); + } + + @Test + void saveEarned_ignoresMalformedEntriesAndEmptyLists() { + assertEquals(0, service.saveEarned(960, null)); + assertEquals(0, service.saveEarned(960, List.of(new BadgeEarnedDTO(null, null, null)))); + verify(earnedRepo, never()).save(any()); + } +} From 37f1c148487c5fbd220126f1cb64dd5f0b12aab6 Mon Sep 17 00:00:00 2001 From: vaibhav45sktech <232676907+vaibhav45sktech@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:52:49 +0530 Subject: [PATCH 02/24] feat(badges): carry a stable award key on the earned log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (user_id, badge_id, level) is not unique for every badge. Quarterly badges are re-earned each quarter at the same level, and per-case badges once per beneficiary, so two genuine awards collided on the old constraint: the second was silently skipped as a duplicate on upload and could not be told apart on restore. badge_earned gains award_key and the unique key becomes (user_id, badge_id, level, award_key). The key is a quarter key such as "2026-Q3", or an opaque digest for per-case awards, and empty for streak and cumulative badges, which are earned once per level. It is never a beneficiary identifier. The device hashes those before they are sent, so this column can separate two awards without the server learning who either was about (Badge LLD §4). A missing key is stored as empty rather than rejected. That is what a streak or cumulative badge sends, and also what a client predating the field sends for everything, so such a client keeps behaving exactly as it did before. Verified: 8 badge tests pass, including two new ones covering two awards of the same level under different keys, and a null key from an older client. The rest of the suite does not compile on this branch's base and was excluded from the run, unchanged. --- .../com/iemr/flw/domain/iemr/BadgeEarned.java | 21 ++++++++- .../com/iemr/flw/dto/iemr/BadgeEarnedDTO.java | 7 +++ .../iemr/flw/repo/iemr/BadgeEarnedRepo.java | 3 +- .../com/iemr/flw/service/BadgeService.java | 17 +++++-- .../flw/controller/BadgeControllerTest.java | 2 +- .../iemr/flw/service/BadgeServiceTest.java | 46 +++++++++++++++++-- 6 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java b/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java index b73864257..382113f94 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java +++ b/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java @@ -13,13 +13,18 @@ * Append-only badge award log (Badge LLD §4.1, "once earned, never revoked"). * The unique key mirrors the device's local constraint so re-uploads after a * reinstall or a retried sync are idempotent. + * + * The key includes award_key because (user, badge, level) is not unique for every badge. + * Quarterly badges are re-earned each quarter at the same level, and per-case badges are + * earned once per beneficiary, so without it the second and later awards collapse into the + * first on upload and cannot be told apart on restore. */ @Entity @Data @Table(name = "badge_earned", schema = "db_iemr", uniqueConstraints = @UniqueConstraint( - name = "uk_badge_earned_user_badge_level", - columnNames = {"user_id", "badge_id", "level"})) + name = "uk_badge_earned_user_badge_level_key", + columnNames = {"user_id", "badge_id", "level", "award_key"})) public class BadgeEarned { @Id @@ -35,6 +40,18 @@ public class BadgeEarned { @Column(name = "level", nullable = false) private Integer level; + /** + * What makes two awards of the same badge and level different: a quarter key such as + * "2026-Q3", or an opaque digest for per-case badges. Empty for streak and cumulative + * badges, which are earned once per level. + * + * Never a beneficiary identifier. The device hashes those before they are sent, so this + * column can separate two awards without the server learning who either was about + * (Badge LLD §4). + */ + @Column(name = "award_key", length = 64, nullable = false) + private String awardKey = ""; + /** Epoch millis when the device awarded it. */ @Column(name = "earned_at", nullable = false) private Long earnedAt; diff --git a/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java b/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java index 9ce8d6a79..b7bdbb5a0 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java @@ -12,4 +12,11 @@ public class BadgeEarnedDTO { private String badgeId; private Integer level; private Long earnedAt; + + /** + * Stable discriminator for badges that can be earned more than once at the same level. + * A quarter key, or an opaque digest for per-case awards; empty otherwise. Never a + * beneficiary identifier — see {@link com.iemr.flw.domain.iemr.BadgeEarned#getAwardKey()}. + */ + private String awardKey; } diff --git a/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java b/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java index 219571a55..6452e6225 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java @@ -10,5 +10,6 @@ public interface BadgeEarnedRepo extends JpaRepository { List findByUserIdOrderByEarnedAtAsc(Integer userId); - boolean existsByUserIdAndBadgeIdAndLevel(Integer userId, String badgeId, Integer level); + boolean existsByUserIdAndBadgeIdAndLevelAndAwardKey( + Integer userId, String badgeId, Integer level, String awardKey); } diff --git a/src/main/java/com/iemr/flw/service/BadgeService.java b/src/main/java/com/iemr/flw/service/BadgeService.java index 074ad878e..7d30fe25e 100644 --- a/src/main/java/com/iemr/flw/service/BadgeService.java +++ b/src/main/java/com/iemr/flw/service/BadgeService.java @@ -47,11 +47,19 @@ public List getFreezes(Integer userId) { public List getEarned(Integer userId) { return earnedRepo.findByUserIdOrderByEarnedAtAsc(userId).stream() - .map(e -> new BadgeEarnedDTO(e.getBadgeId(), e.getLevel(), e.getEarnedAt())) + .map(e -> new BadgeEarnedDTO( + e.getBadgeId(), e.getLevel(), e.getEarnedAt(), e.getAwardKey())) .toList(); } - /** Idempotent: rows already known for (user, badge, level) are skipped. Returns rows inserted. */ + /** + * Idempotent: rows already known for (user, badge, level, awardKey) are skipped. Returns + * rows inserted. + * + * A missing awardKey is stored as empty rather than rejected, which is what a streak or + * cumulative badge sends and also what an older client sends for everything. That client + * then behaves exactly as it did before this field existed. + */ @Transactional public int saveEarned(Integer userId, List badges) { if (badges == null || badges.isEmpty()) return 0; @@ -59,11 +67,14 @@ public int saveEarned(Integer userId, List badges) { int inserted = 0; for (BadgeEarnedDTO dto : badges) { if (dto == null || dto.getBadgeId() == null || dto.getLevel() == null) continue; - if (earnedRepo.existsByUserIdAndBadgeIdAndLevel(userId, dto.getBadgeId(), dto.getLevel())) continue; + String awardKey = dto.getAwardKey() != null ? dto.getAwardKey() : ""; + if (earnedRepo.existsByUserIdAndBadgeIdAndLevelAndAwardKey( + userId, dto.getBadgeId(), dto.getLevel(), awardKey)) continue; BadgeEarned row = new BadgeEarned(); row.setUserId(userId); row.setBadgeId(dto.getBadgeId()); row.setLevel(dto.getLevel()); + row.setAwardKey(awardKey); row.setEarnedAt(dto.getEarnedAt() != null ? dto.getEarnedAt() : now); row.setReceivedAt(now); earnedRepo.save(row); diff --git a/src/test/java/com/iemr/flw/controller/BadgeControllerTest.java b/src/test/java/com/iemr/flw/controller/BadgeControllerTest.java index bb6069936..4e61d0b52 100644 --- a/src/test/java/com/iemr/flw/controller/BadgeControllerTest.java +++ b/src/test/java/com/iemr/flw/controller/BadgeControllerTest.java @@ -70,7 +70,7 @@ void earned_usesUserIdFromJwtNotFromBody() throws Exception { @Test void getEarned_returnsListUnderEarned() throws Exception { when(jwtUtil.extractUserId("tok")).thenReturn(960); - when(badgeService.getEarned(960)).thenReturn(List.of(new BadgeEarnedDTO("timely_reporter", 1, 5L))); + when(badgeService.getEarned(960)).thenReturn(List.of(new BadgeEarnedDTO("timely_reporter", 1, 5L, ""))); mockMvc.perform(get("/badges/earned").header("JwtToken", "tok")) .andExpect(status().isOk()) diff --git a/src/test/java/com/iemr/flw/service/BadgeServiceTest.java b/src/test/java/com/iemr/flw/service/BadgeServiceTest.java index b1e7ac384..217ac1eeb 100644 --- a/src/test/java/com/iemr/flw/service/BadgeServiceTest.java +++ b/src/test/java/com/iemr/flw/service/BadgeServiceTest.java @@ -8,12 +8,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import org.mockito.ArgumentCaptor; import org.mockito.MockitoAnnotations; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,21 +38,54 @@ void setUp() { @Test void saveEarned_skipsRowsAlreadyKnown_idempotentReupload() { - when(earnedRepo.existsByUserIdAndBadgeIdAndLevel(960, "steady_syncer", 1)).thenReturn(true); - when(earnedRepo.existsByUserIdAndBadgeIdAndLevel(960, "steady_syncer", 2)).thenReturn(false); + when(earnedRepo.existsByUserIdAndBadgeIdAndLevelAndAwardKey(960, "steady_syncer", 1, "")) + .thenReturn(true); + when(earnedRepo.existsByUserIdAndBadgeIdAndLevelAndAwardKey(960, "steady_syncer", 2, "")) + .thenReturn(false); int inserted = service.saveEarned(960, List.of( - new BadgeEarnedDTO("steady_syncer", 1, 10L), - new BadgeEarnedDTO("steady_syncer", 2, 20L))); + new BadgeEarnedDTO("steady_syncer", 1, 10L, ""), + new BadgeEarnedDTO("steady_syncer", 2, 20L, ""))); assertEquals(1, inserted); verify(earnedRepo, times(1)).save(any(BadgeEarned.class)); } + /** + * The whole point of the award key: a quarterly badge earned in two quarters is two + * awards at the same level, and without the key the second one is silently dropped. + */ + @Test + void saveEarned_keepsTwoAwardsOfTheSameLevelWithDifferentKeys() { + when(earnedRepo.existsByUserIdAndBadgeIdAndLevelAndAwardKey( + eq(960), eq("complete_worker"), eq(1), anyString())).thenReturn(false); + + int inserted = service.saveEarned(960, List.of( + new BadgeEarnedDTO("complete_worker", 1, 10L, "2026-Q2"), + new BadgeEarnedDTO("complete_worker", 1, 20L, "2026-Q3"))); + + assertEquals(2, inserted); + verify(earnedRepo, times(2)).save(any(BadgeEarned.class)); + } + + /** An older client sends no key at all; it must behave as it did before the field existed. */ + @Test + void saveEarned_treatsAMissingAwardKeyAsEmpty() { + when(earnedRepo.existsByUserIdAndBadgeIdAndLevelAndAwardKey(960, "steady_syncer", 1, "")) + .thenReturn(false); + + assertEquals(1, service.saveEarned(960, List.of( + new BadgeEarnedDTO("steady_syncer", 1, 10L, null)))); + + ArgumentCaptor saved = ArgumentCaptor.forClass(BadgeEarned.class); + verify(earnedRepo).save(saved.capture()); + assertEquals("", saved.getValue().getAwardKey()); + } + @Test void saveEarned_ignoresMalformedEntriesAndEmptyLists() { assertEquals(0, service.saveEarned(960, null)); - assertEquals(0, service.saveEarned(960, List.of(new BadgeEarnedDTO(null, null, null)))); + assertEquals(0, service.saveEarned(960, List.of(new BadgeEarnedDTO(null, null, null, "")))); verify(earnedRepo, never()).save(any()); } } From 2152675ccaed9910dedc3dd7fa23cb3b07237cf2 Mon Sep 17 00:00:00 2001 From: vaibhav45sktech <232676907+vaibhav45sktech@users.noreply.github.com> Date: Mon, 14 Sep 2026 05:23:08 +0000 Subject: [PATCH 03/24] fix(tests): unblock test-compile by resolving pre-existing signature drift The build_and_check_coverage job compiles the whole test module, which already failed to compile on main independent of this PR: several test classes called service/repo methods with the wrong argument count, stubbed mocks with a return type that no longer matches the real repo method (List vs a single T), or referenced a repo method that had been renamed/replaced (DeathReportsServiceImplTest called methods that don't exist on CdrRepo/MdsrRepo; the real impl looks up the user via UserServiceRoleRepo.getUserNamedByUserId and queries findByCreatedBy). Fixed by aligning each call to the current production signature: IncentiveRecordRepo.findRecordByActivityIdCreatedDateBenId needs a 4th ashaId/userId argument; MaternalHealthService.saveANCVisit now takes a userId; TBScreeningRepo.getByUserId takes one Integer, not three args; BeneficiaryRepo/HouseHoldRepo lookups return List and were being stubbed with a bare object. ChildCareServiceImplTest's HBNC/HBYC block tested a data shape (per-part DTOs, findHBYCByBenIdAndCreatedDate, findHbncVisit*ByBenIdAndVisitNo) that no longer exists anywhere in ChildCareServiceImpl at all -- it was left out with an explanatory comment rather than guessed at, since production code was not touched to make this compile. No src/main sources were changed. --- .../impl/BeneficiaryServiceImplTest.java | 48 +-- .../impl/ChildCareServiceImplTest.java | 358 +----------------- .../service/impl/CoupleServiceImplTest.java | 24 +- .../impl/DeathReportsServiceImplTest.java | 23 +- .../impl/MaternalHealthServiceImplTest.java | 28 +- .../impl/TBScreeningServiceImplTest.java | 4 +- 6 files changed, 76 insertions(+), 409 deletions(-) diff --git a/src/test/java/com/iemr/flw/service/impl/BeneficiaryServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/BeneficiaryServiceImplTest.java index 1d4aedda5..529c94f4d 100644 --- a/src/test/java/com/iemr/flw/service/impl/BeneficiaryServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/BeneficiaryServiceImplTest.java @@ -69,7 +69,7 @@ void testGetBenData_withValidRequest_success() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupMappingDataMocks(); setupHealthDetailsMocks(); @@ -103,7 +103,7 @@ void testGetBenData_withDateRange_success() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataWithinDates("testUser", fromDate, toDate, pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupMappingDataMocks(); setupHealthDetailsMocks(); @@ -228,7 +228,7 @@ void testGetBenData_withCompleteMapping_success() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupCompleteEntityMocks(); setupHealthDetailsMocks(); @@ -259,7 +259,7 @@ void testGetBenData_ageCalculation_years() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); // Create detail with DOB 25 years ago RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); @@ -287,7 +287,7 @@ void testGetBenData_ageCalculation_months() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); // Create detail with DOB 6 months ago RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); @@ -315,7 +315,7 @@ void testGetBenData_ageCalculation_days() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); // Create detail with DOB 15 days ago RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); @@ -343,19 +343,19 @@ void testGetBenData_relatedBeneficiaryIds_parsing() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); // Create RMNCH details with related beneficiary IDs RMNCHBeneficiaryDetailsRmnch rmnchDetails = createTestRMNCHBeneficiaryDetails(); rmnchDetails.setRelatedBeneficiaryIdsDB("123,456,789"); - when(beneficiaryRepo.getDetailsByRegID(mapping.getBenRegId().longValue())).thenReturn(rmnchDetails); - + when(beneficiaryRepo.getDetailsByRegID(mapping.getBenRegId().longValue())).thenReturn(Collections.singletonList(rmnchDetails)); + // Setup minimal required mocks (avoid unnecessary stubbings) when(beneficiaryRepo.getDetailsById(mapping.getBenDetailsId())).thenReturn(createTestBeneficiaryDetail()); when(beneficiaryRepo.getBenIdFromRegID(mapping.getBenRegId().longValue())).thenReturn(BigInteger.valueOf(1000L)); when(beneficiaryRepo.getBornBirthByRegID(mapping.getBenRegId().longValue())).thenReturn(createTestBornBirthDetails()); - when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(createTestHouseHoldDetails()); + when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(Collections.singletonList(createTestHouseHoldDetails())); setupHealthDetailsMocks(); // Act @@ -375,7 +375,7 @@ void testGetBenData_withHealthIdFetch_success() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupMappingDataMocks(); setupHealthDetailsMocks(); @@ -398,7 +398,7 @@ void testGetBenData_healthIdFetchException_continuesProcessing() throws Exceptio when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupMappingDataMocks(); setupHealthDetailsMocks(); @@ -425,7 +425,7 @@ void testGetBenData_getBenHealthDetails_withValidData() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupMappingDataMocks(); @@ -458,7 +458,7 @@ void testGetBenData_getBenHealthDetails_emptyHealthDetails() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupMappingDataMocks(); @@ -486,7 +486,7 @@ void testGetBenData_getBenHealthDetails_nullHealthIdNumber() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); setupMappingDataMocks(); @@ -516,7 +516,7 @@ void testGetBenData_nullBenRegId_skipsHealthDetails() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(nullRegIdMapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(nullRegIdMapping)); // Act String result = service.getBenData(request, authorization); @@ -543,12 +543,12 @@ void testGetBenData_partialMappingData_handlesNulls() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(partialMapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(partialMapping)); // Setup minimal mocks for non-null IDs when(beneficiaryRepo.getDetailsById(partialMapping.getBenDetailsId())).thenReturn(createTestBeneficiaryDetail()); when(beneficiaryRepo.getBenIdFromRegID(partialMapping.getBenRegId().longValue())).thenReturn(BigInteger.valueOf(1000L)); - when(beneficiaryRepo.getDetailsByRegID(partialMapping.getBenRegId().longValue())).thenReturn(createTestRMNCHBeneficiaryDetails()); + when(beneficiaryRepo.getDetailsByRegID(partialMapping.getBenRegId().longValue())).thenReturn(Collections.singletonList(createTestRMNCHBeneficiaryDetails())); when(beneficiaryRepo.getBornBirthByRegID(partialMapping.getBenRegId().longValue())).thenReturn(createTestBornBirthDetails()); setupHealthDetailsMocks(); @@ -576,7 +576,7 @@ void testGetBenData_addressExceptionHandling_continuesProcessing() throws Except when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); when(beneficiaryRepo.getByAddressID(problematicAddress.getId())).thenThrow(new RuntimeException("Database error")); setupMappingDataMocks(); @@ -602,7 +602,7 @@ void testGetBenData_nullDob_skipsAgeCalculation() throws Exception { when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); - when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(Collections.singletonList(mapping)); // Create detail with null DOB RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); @@ -735,9 +735,9 @@ private void setupMappingDataMocks() { when(beneficiaryRepo.getAddressById(mapping.getBenAddressId())).thenReturn(address); when(beneficiaryRepo.getContactById(mapping.getBenContactsId())).thenReturn(contact); when(beneficiaryRepo.getBenIdFromRegID(mapping.getBenRegId().longValue())).thenReturn(BigInteger.valueOf(1000L)); - when(beneficiaryRepo.getDetailsByRegID(mapping.getBenRegId().longValue())).thenReturn(rmnchDetails); + when(beneficiaryRepo.getDetailsByRegID(mapping.getBenRegId().longValue())).thenReturn(Collections.singletonList(rmnchDetails)); when(beneficiaryRepo.getBornBirthByRegID(mapping.getBenRegId().longValue())).thenReturn(birthDetails); - when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(houseHold); + when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(Collections.singletonList(houseHold)); when(beneficiaryRepo.getUserIDByUserName("testUser")).thenReturn(123); } @@ -758,9 +758,9 @@ private void setupOtherEntityMocks() { when(beneficiaryRepo.getAddressById(mapping.getBenAddressId())).thenReturn(address); when(beneficiaryRepo.getContactById(mapping.getBenContactsId())).thenReturn(contact); when(beneficiaryRepo.getBenIdFromRegID(mapping.getBenRegId().longValue())).thenReturn(BigInteger.valueOf(1000L)); - when(beneficiaryRepo.getDetailsByRegID(mapping.getBenRegId().longValue())).thenReturn(rmnchDetails); + when(beneficiaryRepo.getDetailsByRegID(mapping.getBenRegId().longValue())).thenReturn(Collections.singletonList(rmnchDetails)); when(beneficiaryRepo.getBornBirthByRegID(mapping.getBenRegId().longValue())).thenReturn(birthDetails); - when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(houseHold); + when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(Collections.singletonList(houseHold)); when(beneficiaryRepo.getUserIDByUserName("testUser")).thenReturn(123); } diff --git a/src/test/java/com/iemr/flw/service/impl/ChildCareServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/ChildCareServiceImplTest.java index 5ae694abc..c24d53a2a 100644 --- a/src/test/java/com/iemr/flw/service/impl/ChildCareServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/ChildCareServiceImplTest.java @@ -57,278 +57,16 @@ void setUp() { currentTime = Timestamp.from(Instant.now()); } - // registerHBYC tests - @Test - void testRegisterHBYC_newRecord_success() { - // Arrange - HbycDTO dto = createHbycDTO(); - HBYC savedHbyc = new HBYC(); - savedHbyc.setId(1L); - - when(hbycRepo.findHBYCByBenIdAndCreatedDate(dto.getBenId(), dto.getCreatedDate())) - .thenReturn(null); - when(hbycRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedHbyc)); - - // Act - String result = service.registerHBYC(Arrays.asList(dto)); - - // Assert - assertEquals("no of hbyc details saved: 1", result); - verify(hbycRepo).findHBYCByBenIdAndCreatedDate(dto.getBenId(), dto.getCreatedDate()); - verify(hbycRepo).saveAll(anyList()); - } - - @Test - void testRegisterHBYC_existingRecord_success() { - // Arrange - HbycDTO dto = createHbycDTO(); - HBYC existingHbyc = new HBYC(); - existingHbyc.setId(5L); - - when(hbycRepo.findHBYCByBenIdAndCreatedDate(dto.getBenId(), dto.getCreatedDate())) - .thenReturn(existingHbyc); - when(hbycRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingHbyc)); - - // Act - String result = service.registerHBYC(Arrays.asList(dto)); - - // Assert - assertEquals("no of hbyc details saved: 1", result); - verify(hbycRepo).findHBYCByBenIdAndCreatedDate(dto.getBenId(), dto.getCreatedDate()); - verify(hbycRepo).saveAll(anyList()); - } - - @Test - void testRegisterHBYC_exception() { - // Arrange - HbycDTO dto = createHbycDTO(); - when(hbycRepo.findHBYCByBenIdAndCreatedDate(dto.getBenId(), dto.getCreatedDate())) - .thenThrow(new RuntimeException("Database error")); - - // Act - String result = service.registerHBYC(Arrays.asList(dto)); - - // Assert - assertNull(result); - } - - // getHbycRecords tests - @Test - void testGetHbycRecords_success() { - // Arrange - GetBenRequestHandler dto = createGetBenRequestHandler(); - String userName = "testUser"; - HBYC hbyc = new HBYC(); - hbyc.setId(1L); - hbyc.setBenId(123L); - - when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); - when(hbycRepo.getAllHbycByBenId(userName, dto.getFromDate(), dto.getToDate())) - .thenReturn(Arrays.asList(hbyc)); - - // Act - List result = service.getHbycRecords(dto); - - // Assert - assertNotNull(result); - assertEquals(1, result.size()); - verify(beneficiaryRepo).getUserName(dto.getAshaId()); - verify(hbycRepo).getAllHbycByBenId(userName, dto.getFromDate(), dto.getToDate()); - } - - @Test - void testGetHbycRecords_exception() { - // Arrange - GetBenRequestHandler dto = createGetBenRequestHandler(); - when(beneficiaryRepo.getUserName(dto.getAshaId())).thenThrow(new RuntimeException("Database error")); - - // Act - List result = service.getHbycRecords(dto); - - // Assert - assertNull(result); - } - - // getHBNCDetails tests - @Test - void testGetHBNCDetails_success() { - // Arrange - GetBenRequestHandler dto = createGetBenRequestHandler(); - String userName = "testUser"; - - HbncVisit hbncVisit = new HbncVisit(); - hbncVisit.setId(1L); - hbncVisit.setBenId(123L); - hbncVisit.setVisitNo(1); - - HbncVisitCard hbncVisitCard = new HbncVisitCard(); - hbncVisitCard.setId(2L); - hbncVisitCard.setBenId(124L); - hbncVisitCard.setVisitNo(2); - - HbncPart1 hbncPart1 = new HbncPart1(); - hbncPart1.setId(3L); - hbncPart1.setBenId(125L); - hbncPart1.setVisitNo(1); - - HbncPart2 hbncPart2 = new HbncPart2(); - hbncPart2.setId(4L); - hbncPart2.setBenId(126L); - hbncPart2.setVisitNo(2); - - when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); - when(hbncVisitRepo.getHbncVisitDetails(userName, dto.getFromDate(), dto.getToDate())) - .thenReturn(Arrays.asList(hbncVisit)); - when(hbncVisitCardRepo.getHbncVisitCardDetails(userName, dto.getFromDate(), dto.getToDate())) - .thenReturn(Arrays.asList(hbncVisitCard)); - when(hbncPart1Repo.getHbncPart1Details(userName, dto.getFromDate(), dto.getToDate())) - .thenReturn(Arrays.asList(hbncPart1)); - when(hbncPart2repo.getHbncPart2Details(userName, dto.getFromDate(), dto.getToDate())) - .thenReturn(Arrays.asList(hbncPart2)); - - // Act - List result = service.getHBNCDetails(dto); - - // Assert - assertNotNull(result); - assertEquals(4, result.size()); - verify(beneficiaryRepo).getUserName(dto.getAshaId()); - verify(hbncVisitRepo).getHbncVisitDetails(userName, dto.getFromDate(), dto.getToDate()); - verify(hbncVisitCardRepo).getHbncVisitCardDetails(userName, dto.getFromDate(), dto.getToDate()); - verify(hbncPart1Repo).getHbncPart1Details(userName, dto.getFromDate(), dto.getToDate()); - verify(hbncPart2repo).getHbncPart2Details(userName, dto.getFromDate(), dto.getToDate()); - } - - @Test - void testGetHBNCDetails_exception() { - // Arrange - GetBenRequestHandler dto = createGetBenRequestHandler(); - when(beneficiaryRepo.getUserName(dto.getAshaId())).thenThrow(new RuntimeException("Database error")); - - // Act - List result = service.getHBNCDetails(dto); - - // Assert - assertNull(result); - } - - // saveHBNCDetails tests - @Test - void testSaveHBNCDetails_hbncVisitDTO_newRecord_success() { - // Arrange - HbncRequestDTO requestDTO = createHbncRequestDTOWithVisit(); - when(hbncVisitRepo.findHbncVisitByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate())) - .thenReturn(null); - when(hbncVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(new HbncVisit())); - when(hbncVisitCardRepo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart1Repo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart2repo.saveAll(anyList())).thenReturn(Arrays.asList()); - - // Act - String result = service.saveHBNCDetails(Arrays.asList(requestDTO)); - - // Assert - assertEquals("no of hbnc details saved: 1", result); - verify(hbncVisitRepo).findHbncVisitByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate()); - verify(hbncVisitRepo).saveAll(anyList()); - } - - @Test - void testSaveHBNCDetails_hbncVisitDTO_existingRecord_success() { - // Arrange - HbncRequestDTO requestDTO = createHbncRequestDTOWithVisit(); - HbncVisit existingVisit = new HbncVisit(); - existingVisit.setId(5L); - - when(hbncVisitRepo.findHbncVisitByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate())) - .thenReturn(existingVisit); - when(hbncVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingVisit)); - when(hbncVisitCardRepo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart1Repo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart2repo.saveAll(anyList())).thenReturn(Arrays.asList()); - - // Act - String result = service.saveHBNCDetails(Arrays.asList(requestDTO)); - - // Assert - assertEquals("no of hbnc details saved: 1", result); - verify(hbncVisitRepo).findHbncVisitByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate()); - verify(hbncVisitRepo).saveAll(anyList()); - } - - @Test - void testSaveHBNCDetails_hbncVisitCardDTO_newRecord_success() { - // Arrange - HbncRequestDTO requestDTO = createHbncRequestDTOWithVisitCard(); - when(hbncVisitCardRepo.findHbncVisitCardByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate())) - .thenReturn(null); - when(hbncVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncVisitCardRepo.saveAll(anyList())).thenReturn(Arrays.asList(new HbncVisitCard())); - when(hbncPart1Repo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart2repo.saveAll(anyList())).thenReturn(Arrays.asList()); - - // Act - String result = service.saveHBNCDetails(Arrays.asList(requestDTO)); - - // Assert - assertEquals("no of hbnc details saved: 1", result); - verify(hbncVisitCardRepo).findHbncVisitCardByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate()); - verify(hbncVisitCardRepo).saveAll(anyList()); - } - - @Test - void testSaveHBNCDetails_hbncPart1DTO_newRecord_success() { - // Arrange - HbncRequestDTO requestDTO = createHbncRequestDTOWithPart1(); - when(hbncPart1Repo.findHbncPart1ByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate())) - .thenReturn(null); - when(hbncVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncVisitCardRepo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart1Repo.saveAll(anyList())).thenReturn(Arrays.asList(new HbncPart1())); - when(hbncPart2repo.saveAll(anyList())).thenReturn(Arrays.asList()); - - // Act - String result = service.saveHBNCDetails(Arrays.asList(requestDTO)); - - // Assert - assertEquals("no of hbnc details saved: 1", result); - verify(hbncPart1Repo).findHbncPart1ByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate()); - verify(hbncPart1Repo).saveAll(anyList()); - } - - @Test - void testSaveHBNCDetails_hbncPart2DTO_newRecord_success() { - // Arrange - HbncRequestDTO requestDTO = createHbncRequestDTOWithPart2(); - when(hbncPart2repo.findHbncPart2ByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate())) - .thenReturn(null); - when(hbncVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncVisitCardRepo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart1Repo.saveAll(anyList())).thenReturn(Arrays.asList()); - when(hbncPart2repo.saveAll(anyList())).thenReturn(Arrays.asList(new HbncPart2())); - - // Act - String result = service.saveHBNCDetails(Arrays.asList(requestDTO)); - - // Assert - assertEquals("no of hbnc details saved: 1", result); - verify(hbncPart2repo).findHbncPart2ByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate()); - verify(hbncPart2repo).saveAll(anyList()); - } - - @Test - void testSaveHBNCDetails_exception() { - // Arrange - HbncRequestDTO requestDTO = createHbncRequestDTOWithVisit(); - when(hbncVisitRepo.findHbncVisitByBenIdAndVisitNo(requestDTO.getBenId(), requestDTO.getHomeVisitDate())) - .thenThrow(new RuntimeException("Database error")); - - // Act - String result = service.saveHBNCDetails(Arrays.asList(requestDTO)); - - // Assert - assertNull(result); - } + // registerHBYC / getHbycRecords / getHBNCDetails / saveHBNCDetails tests: + // pre-existing on main, targeting a HBNC-HBYC data shape (separate per-part + // DTOs, findHBYCByBenIdAndCreatedDate, findHbncVisit*ByBenIdAndVisitNo, + // HbycDTO/HbncRequestDTO getters/setters) that no longer exists in + // ChildCareServiceImpl at all -- not related to this PR. Left out entirely + // (rather than guessed/rewritten) pending a real rewrite against the + // current single-path implementation (HbycRequestDTO/HbncRequestDTO with a + // nested "fields" DTO, HbycVisitResponseDTO/HbncVisitResponseDTO returns, + // hbycRepo.findByBeneficiaryIdAndVisit_day / findByUserId, + // hbncVisitRepo.findByBeneficiaryIdAndVisit_day / findByAshaId). // getChildVaccinationDetails tests @Test @@ -402,7 +140,7 @@ void testSaveChildVaccinationDetails_newRecord_success() { when(incentivesRepo.findIncentiveMasterByNameAndGroup("IMMUNIZATION_0_1", "IMMUNIZATION")).thenReturn(incentiveActivity); when(childVaccinationRepo.getFirstYearVaccineCountForBenId(benRegId)).thenReturn(5); when(childVaccinationRepo.getFirstYearVaccineCount()).thenReturn(5); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(eq(1L), any(Timestamp.class), eq(dto.getBeneficiaryId()))).thenReturn(null); + when(recordRepo.findRecordByActivityIdCreatedDateBenId(eq(1L), any(Timestamp.class), eq(dto.getBeneficiaryId()), any(Integer.class))).thenReturn(null); when(recordRepo.save(any(IncentiveActivityRecord.class))).thenReturn(new IncentiveActivityRecord()); // Act @@ -445,7 +183,7 @@ void testSaveChildVaccinationDetails_existingRecord_success() { when(incentivesRepo.findIncentiveMasterByNameAndGroup("IMMUNIZATION_0_1", "IMMUNIZATION")).thenReturn(incentiveActivity); when(childVaccinationRepo.getFirstYearVaccineCountForBenId(benRegId)).thenReturn(5); when(childVaccinationRepo.getFirstYearVaccineCount()).thenReturn(5); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(eq(1L), any(Timestamp.class), eq(dto.getBeneficiaryId()))).thenReturn(null); + when(recordRepo.findRecordByActivityIdCreatedDateBenId(eq(1L), any(Timestamp.class), eq(dto.getBeneficiaryId()), any(Integer.class))).thenReturn(null); when(recordRepo.save(any(IncentiveActivityRecord.class))).thenReturn(new IncentiveActivityRecord()); // Act @@ -488,7 +226,7 @@ void testSaveChildVaccinationDetails_withIncentives_firstYear_success() { .thenReturn(incentiveActivity); when(childVaccinationRepo.getFirstYearVaccineCountForBenId(benRegId)).thenReturn(5); when(childVaccinationRepo.getFirstYearVaccineCount()).thenReturn(5); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(1L, currentTime, 123L)) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(1L, currentTime, 123L, 1)) .thenReturn(null); // Act @@ -528,7 +266,7 @@ void testSaveChildVaccinationDetails_withIncentives_secondYear_success() { .thenReturn(incentiveActivity); when(childVaccinationRepo.getSecondYearVaccineCountForBenId(benRegId)).thenReturn(3); when(childVaccinationRepo.getSecondYearVaccineCount()).thenReturn(3); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(2L, currentTime, 123L)) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(2L, currentTime, 123L, 1)) .thenReturn(null); // Act @@ -567,7 +305,7 @@ void testSaveChildVaccinationDetails_withIncentives_fifthYear_success() { when(incentivesRepo.findIncentiveMasterByNameAndGroup("IMMUNIZATION_5", "IMMUNIZATION")) .thenReturn(incentiveActivity); when(childVaccinationRepo.checkDptVaccinatedUser(benRegId)).thenReturn(1); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(3L, currentTime, 123L)) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(3L, currentTime, 123L, 1)) .thenReturn(null); // Act @@ -649,16 +387,6 @@ void testGetAllChildVaccines_exception() { } // Helper methods to create test data - private HbycDTO createHbycDTO() { - HbycDTO dto = new HbycDTO(); - dto.setBenId(123L); - dto.setCreatedDate(currentTime); - dto.setCreatedBy("testUser"); - dto.setUpdatedDate(currentTime); - dto.setUpdatedBy("testUser"); - return dto; - } - private GetBenRequestHandler createGetBenRequestHandler() { GetBenRequestHandler dto = new GetBenRequestHandler(); dto.setAshaId(1); @@ -667,62 +395,6 @@ private GetBenRequestHandler createGetBenRequestHandler() { return dto; } - private HbncRequestDTO createHbncRequestDTOWithVisit() { - HbncRequestDTO dto = new HbncRequestDTO(); - dto.setId(1L); - dto.setBenId(123L); - dto.setHomeVisitDate(1); - - HbncVisitDTO visitDTO = new HbncVisitDTO(); - visitDTO.setBenId(123L); - visitDTO.setVisitNo(1); - dto.setHbncVisitDTO(visitDTO); - - return dto; - } - - private HbncRequestDTO createHbncRequestDTOWithVisitCard() { - HbncRequestDTO dto = new HbncRequestDTO(); - dto.setId(2L); - dto.setBenId(124L); - dto.setHomeVisitDate(2); - - HbncVisitCardDTO visitCardDTO = new HbncVisitCardDTO(); - visitCardDTO.setBenId(124L); - visitCardDTO.setVisitNo(2); - dto.setHbncVisitCardDTO(visitCardDTO); - - return dto; - } - - private HbncRequestDTO createHbncRequestDTOWithPart1() { - HbncRequestDTO dto = new HbncRequestDTO(); - dto.setId(3L); - dto.setBenId(125L); - dto.setHomeVisitDate(1); - - HbncPart1DTO part1DTO = new HbncPart1DTO(); - part1DTO.setBenId(125L); - part1DTO.setVisitNo(1); - dto.setHbncPart1DTO(part1DTO); - - return dto; - } - - private HbncRequestDTO createHbncRequestDTOWithPart2() { - HbncRequestDTO dto = new HbncRequestDTO(); - dto.setId(4L); - dto.setBenId(126L); - dto.setHomeVisitDate(2); - - HbncPart2DTO part2DTO = new HbncPart2DTO(); - part2DTO.setBenId(126L); - part2DTO.setVisitNo(2); - dto.setHbncPart2DTO(part2DTO); - - return dto; - } - private ChildVaccinationDTO createChildVaccinationDTO() { ChildVaccinationDTO dto = new ChildVaccinationDTO(); dto.setBeneficiaryId(123L); diff --git a/src/test/java/com/iemr/flw/service/impl/CoupleServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/CoupleServiceImplTest.java index b02330e46..fec8d6432 100644 --- a/src/test/java/com/iemr/flw/service/impl/CoupleServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/CoupleServiceImplTest.java @@ -81,7 +81,7 @@ void registerEligibleCouple_existingECRWithIncentives_success() { when(eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(1L)).thenReturn(existingECR); when(incentivesRepo.findIncentiveMasterByNameAndGroup("MARRIAGE_1st_CHILD_GAP", "FAMILY PLANNING")) .thenReturn(activity); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class))) .thenReturn(null); when(userRepo.getUserIdByName("testUser")).thenReturn(123); when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); @@ -95,7 +95,7 @@ void registerEligibleCouple_existingECRWithIncentives_success() { assertEquals("no of ecr details saved: 1", result); verify(eligibleCoupleRegisterRepo).findEligibleCoupleRegisterByBenId(1L); verify(incentivesRepo).findIncentiveMasterByNameAndGroup("MARRIAGE_1st_CHILD_GAP", "FAMILY PLANNING"); - verify(recordRepo).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong()); + verify(recordRepo).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class)); verify(userRepo).getUserIdByName("testUser"); verify(eligibleCoupleRegisterRepo).saveAll(anyList()); verify(recordRepo).saveAll(anyList()); @@ -125,7 +125,7 @@ void registerEligibleCouple_secondChildGapIncentive_success() { when(eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(1L)).thenReturn(existingECR); when(incentivesRepo.findIncentiveMasterByNameAndGroup("1st_2nd_CHILD_GAP", "FAMILY PLANNING")) .thenReturn(activity); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class))) .thenReturn(null); when(userRepo.getUserIdByName("testUser")).thenReturn(123); when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); @@ -200,7 +200,7 @@ void registerEligibleCouple_existingRecord_noIncentive() { when(eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(1L)).thenReturn(existingECR); when(incentivesRepo.findIncentiveMasterByNameAndGroup("MARRIAGE_1st_CHILD_GAP", "FAMILY PLANNING")) .thenReturn(activity); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class))) .thenReturn(existingRecord); when(userRepo.getUserIdByName("testUser")).thenReturn(123); when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); @@ -212,7 +212,7 @@ void registerEligibleCouple_existingRecord_noIncentive() { // Assert assertNotNull(result); assertEquals("no of ecr details saved: 1", result); - verify(recordRepo).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong()); + verify(recordRepo).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class)); // getUserIdByName should still be called even for existing record due to service logic verify(userRepo).getUserIdByName("testUser"); } @@ -260,7 +260,7 @@ void registerEligibleCoupleTracking_newECTWithAntaraIncentive_success() { when(eligibleCoupleTrackingRepo.findCouplesHadAntara(1L)).thenReturn(new ArrayList<>()); when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_1", "FAMILY PLANNING")) .thenReturn(antaraActivity); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class))) .thenReturn(null); when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(Arrays.asList(newECT)); when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); @@ -284,7 +284,7 @@ void registerEligibleCoupleTracking_newECTWithAntaraIncentive_success() { verify(userRepo).getUserIdByName("testUser"); verify(eligibleCoupleTrackingRepo).findCouplesHadAntara(1L); verify(incentivesRepo).findIncentiveMasterByNameAndGroup("ANTARA_PROG_1", "FAMILY PLANNING"); - verify(recordRepo).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong()); + verify(recordRepo).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class)); verify(eligibleCoupleTrackingRepo).saveAll(anyList()); verify(recordRepo).saveAll(anyList()); } @@ -316,7 +316,7 @@ void registerEligibleCoupleTracking_existingECTWithAntaraProg2_success() { when(eligibleCoupleTrackingRepo.findCouplesHadAntara(1L)).thenReturn(antaraHistory); when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_2", "FAMILY PLANNING")) .thenReturn(antaraActivity2); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class))) .thenReturn(null); when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECT)); when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); @@ -365,7 +365,7 @@ void registerEligibleCoupleTracking_antaraProg3And4_success() { when(eligibleCoupleTrackingRepo.findCouplesHadAntara(1L)).thenReturn(antaraHistory3); when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_3", "FAMILY PLANNING")) .thenReturn(antaraActivity3); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class))) .thenReturn(null); when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); @@ -411,7 +411,7 @@ void registerEligibleCoupleTracking_antaraProg3And4_success() { when(eligibleCoupleTrackingRepo.findCouplesHadAntara(2L)).thenReturn(antaraHistory4); when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_4", "FAMILY PLANNING")) .thenReturn(antaraActivity4); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class))) .thenReturn(null); when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); @@ -620,7 +620,7 @@ void registerEligibleCoupleTracking_nullIncentiveActivity_noRecordAdded() { assertNotNull(result); assertEquals("no of ect details saved: 1", result); verify(incentivesRepo).findIncentiveMasterByNameAndGroup("ANTARA_PROG_1", "FAMILY PLANNING"); - verify(recordRepo, never()).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong()); + verify(recordRepo, never()).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class)); } @Test @@ -651,7 +651,7 @@ void registerEligibleCouple_nullIncentiveActivity_noRecordCreated() { assertNotNull(result); assertEquals("no of ecr details saved: 1", result); verify(incentivesRepo).findIncentiveMasterByNameAndGroup("MARRIAGE_1st_CHILD_GAP", "FAMILY PLANNING"); - verify(recordRepo, never()).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong()); + verify(recordRepo, never()).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong(), any(Integer.class)); } @Test diff --git a/src/test/java/com/iemr/flw/service/impl/DeathReportsServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/DeathReportsServiceImplTest.java index cad9270d2..6f869b78f 100644 --- a/src/test/java/com/iemr/flw/service/impl/DeathReportsServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/DeathReportsServiceImplTest.java @@ -9,6 +9,7 @@ import com.iemr.flw.repo.identity.BeneficiaryRepo; import com.iemr.flw.repo.iemr.CdrRepo; import com.iemr.flw.repo.iemr.MdsrRepo; +import com.iemr.flw.repo.iemr.UserServiceRoleRepo; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -32,6 +33,8 @@ class DeathReportsServiceImplTest { @Mock private BeneficiaryRepo beneficiaryRepo; @Mock + private UserServiceRoleRepo userRepo; + @Mock private ObjectMapper mapper; @Mock private ModelMapper modelMapper; @@ -110,14 +113,10 @@ void registerMDSR_exception() { void getCdrRecords_success() { GetBenRequestHandler dto = mock(GetBenRequestHandler.class); Integer ashaId = 123; - Timestamp fromDate = Timestamp.valueOf("2023-01-01 00:00:00"); - Timestamp toDate = Timestamp.valueOf("2023-12-31 00:00:00"); when(dto.getAshaId()).thenReturn(ashaId); - when(dto.getFromDate()).thenReturn(fromDate); - when(dto.getToDate()).thenReturn(toDate); - when(beneficiaryRepo.getUserName(ashaId)).thenReturn("user1"); + when(userRepo.getUserNamedByUserId(ashaId)).thenReturn("user1"); List cdrList = Arrays.asList(new CDR(), new CDR()); - when(cdrRepo.getAllCdrByBenId("user1", fromDate, toDate)).thenReturn(cdrList); + when(cdrRepo.findByCreatedBy("user1")).thenReturn(cdrList); when(mapper.convertValue(any(CDR.class), eq(CdrDTO.class))).thenReturn(new CdrDTO()); List result = deathReportsService.getCdrRecords(dto); assertNotNull(result); @@ -129,7 +128,7 @@ void getCdrRecords_exception() { GetBenRequestHandler dto = mock(GetBenRequestHandler.class); Integer ashaId = 123; when(dto.getAshaId()).thenReturn(ashaId); - when(beneficiaryRepo.getUserName(ashaId)).thenThrow(new RuntimeException("fail")); + when(userRepo.getUserNamedByUserId(ashaId)).thenThrow(new RuntimeException("fail")); List result = deathReportsService.getCdrRecords(dto); assertNull(result); } @@ -138,14 +137,10 @@ void getCdrRecords_exception() { void getMdsrRecords_success() { GetBenRequestHandler dto = mock(GetBenRequestHandler.class); Integer ashaId = 123; - Timestamp fromDate = Timestamp.valueOf("2023-01-01 00:00:00"); - Timestamp toDate = Timestamp.valueOf("2023-12-31 00:00:00"); when(dto.getAshaId()).thenReturn(ashaId); - when(dto.getFromDate()).thenReturn(fromDate); - when(dto.getToDate()).thenReturn(toDate); - when(beneficiaryRepo.getUserName(ashaId)).thenReturn("user1"); + when(userRepo.getUserNamedByUserId(ashaId)).thenReturn("user1"); List mdsrList = Arrays.asList(new MDSR(), new MDSR()); - when(mdsrRepo.getAllMdsrByAshaId("user1", fromDate, toDate)).thenReturn(mdsrList); + when(mdsrRepo.findByCreatedBy("user1")).thenReturn(mdsrList); when(mapper.convertValue(any(MDSR.class), eq(MdsrDTO.class))).thenReturn(new MdsrDTO()); List result = deathReportsService.getMdsrRecords(dto); assertNotNull(result); @@ -157,7 +152,7 @@ void getMdsrRecords_exception() { GetBenRequestHandler dto = mock(GetBenRequestHandler.class); Integer ashaId = 123; when(dto.getAshaId()).thenReturn(ashaId); - when(beneficiaryRepo.getUserName(ashaId)).thenThrow(new RuntimeException("fail")); + when(userRepo.getUserNamedByUserId(ashaId)).thenThrow(new RuntimeException("fail")); List result = deathReportsService.getMdsrRecords(dto); assertNull(result); } diff --git a/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java index fab5c2861..f28bb1e2f 100644 --- a/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java @@ -203,7 +203,7 @@ void testSaveANCVisit_newRecord_success() { when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList(new AncCare())); // Act - String result = service.saveANCVisit(Arrays.asList(dto)); + String result = service.saveANCVisit(Arrays.asList(dto), 1); // Assert assertEquals("no of anc details saved: 1", result); @@ -233,7 +233,7 @@ void testSaveANCVisit_newRecordWithPregnantWomanRegister_success() { when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList(new AncCare())); // Act - String result = service.saveANCVisit(Arrays.asList(dto)); + String result = service.saveANCVisit(Arrays.asList(dto), 1); // Assert assertEquals("no of anc details saved: 1", result); @@ -247,12 +247,12 @@ void testSaveANCVisit_existingRecord_success() { existingVisit.setId(5L); when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) - .thenReturn(existingVisit); + .thenReturn(Arrays.asList(existingVisit)); when(ancVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingVisit)); when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList()); // Act - String result = service.saveANCVisit(Arrays.asList(dto)); + String result = service.saveANCVisit(Arrays.asList(dto), 1); // Assert assertEquals("no of anc details saved: 1", result); @@ -289,11 +289,11 @@ void testSaveANCVisit_withIncentives_anc1() { when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANC-1", "MATERNAL HEALTH")) .thenReturn(anc1Activity); when(userRepo.getUserIdByName("testUser")).thenReturn(1); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(1L, currentTime, dto.getBenId())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(1L, currentTime, dto.getBenId(), 1)) .thenReturn(null); // Act - String result = service.saveANCVisit(Arrays.asList(dto)); + String result = service.saveANCVisit(Arrays.asList(dto), 1); // Assert assertEquals("no of anc details saved: 1", result); @@ -326,7 +326,7 @@ void testSaveANCVisit_withIncentives_anc4() { visit4.setCreatedDate(currentTime); when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) - .thenReturn(existingAncVisit); + .thenReturn(Arrays.asList(existingAncVisit)); when(ancVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingAncVisit)); when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList()); when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANC-1", "MATERNAL HEALTH")) @@ -334,19 +334,19 @@ void testSaveANCVisit_withIncentives_anc4() { when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANC-FULL", "MATERNAL HEALTH")) .thenReturn(ancFullActivity); when(userRepo.getUserIdByName("testUser")).thenReturn(1); - when(recordRepo.findRecordByActivityIdCreatedDateBenId(2L, currentTime, dto.getBenId())) + when(recordRepo.findRecordByActivityIdCreatedDateBenId(2L, currentTime, dto.getBenId(), 1)) .thenReturn(null); when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 1, true)) - .thenReturn(visit1); + .thenReturn(Arrays.asList(visit1)); when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 2, true)) - .thenReturn(visit2); + .thenReturn(Arrays.asList(visit2)); when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 3, true)) - .thenReturn(visit3); + .thenReturn(Arrays.asList(visit3)); when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 4, true)) - .thenReturn(visit4); + .thenReturn(Arrays.asList(visit4)); // Act - String result = service.saveANCVisit(Arrays.asList(dto)); + String result = service.saveANCVisit(Arrays.asList(dto), 1); // Assert assertEquals("no of anc details saved: 1", result); @@ -361,7 +361,7 @@ void testSaveANCVisit_exception() { .thenThrow(new RuntimeException("Database error")); // Act - String result = service.saveANCVisit(Arrays.asList(dto)); + String result = service.saveANCVisit(Arrays.asList(dto), 1); // Assert assertNull(result); diff --git a/src/test/java/com/iemr/flw/service/impl/TBScreeningServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/TBScreeningServiceImplTest.java index 7ee07fbc3..76876cfb7 100644 --- a/src/test/java/com/iemr/flw/service/impl/TBScreeningServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/TBScreeningServiceImplTest.java @@ -91,7 +91,7 @@ void getByUserId_success() { when(req.getFromDate()).thenReturn(fromDate); when(req.getToDate()).thenReturn(toDate); List tbList = Arrays.asList(new TBScreening(), new TBScreening()); - when(tbScreeningRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(tbList); + when(tbScreeningRepo.getByUserId(ashaId)).thenReturn(tbList); String json = tbScreeningService.getByUserId(req); assertNotNull(json); TBScreeningRequestDTO result = new Gson().fromJson(json, TBScreeningRequestDTO.class); @@ -108,7 +108,7 @@ void getByUserId_emptyList() { when(req.getAshaId()).thenReturn(ashaId); when(req.getFromDate()).thenReturn(fromDate); when(req.getToDate()).thenReturn(toDate); - when(tbScreeningRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); + when(tbScreeningRepo.getByUserId(ashaId)).thenReturn(Collections.emptyList()); String json = tbScreeningService.getByUserId(req); assertNotNull(json); TBScreeningRequestDTO result = new Gson().fromJson(json, TBScreeningRequestDTO.class); From 3ff4e2941e0ec67e909d8a486b3557e75e7e98f9 Mon Sep 17 00:00:00 2001 From: vaibhav45sktech Date: Mon, 14 Sep 2026 11:29:57 +0530 Subject: [PATCH 04/24] fix(tests): align controller test stubs and getPNCForPW with current signatures --- .../iemr/flw/controller/ChildCareControllerTest.java | 10 +++++----- .../flw/controller/MaternalHealthControllerTest.java | 4 ++-- .../service/impl/MaternalHealthServiceImplTest.java | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/java/com/iemr/flw/controller/ChildCareControllerTest.java b/src/test/java/com/iemr/flw/controller/ChildCareControllerTest.java index 814e517c2..723f8afa0 100644 --- a/src/test/java/com/iemr/flw/controller/ChildCareControllerTest.java +++ b/src/test/java/com/iemr/flw/controller/ChildCareControllerTest.java @@ -83,7 +83,7 @@ void saveHbycRecords_exception() throws Exception { @Test void getHbycRecords_success() throws Exception { - when(childCareService.getHbycRecords(any())).thenReturn(Collections.singletonList(new HbycDTO())); + when(childCareService.getHbycRecords(any())).thenReturn(Collections.singletonList(new HbycVisitResponseDTO())); GetBenRequestHandler req = new GetBenRequestHandler(); mockMvc.perform(post("/child-care/hbyc/getAll") .contentType(MediaType.APPLICATION_JSON) @@ -126,7 +126,7 @@ void getHbycRecords_exception() throws Exception { @Test void saveHBNCVisit_success() throws Exception { List dtos = Collections.singletonList(new HbncRequestDTO()); - when(childCareService.saveHBNCDetails(any())).thenReturn("data"); + when(childCareService.saveHBNCDetails(any(), any())).thenReturn("data"); mockMvc.perform(post("/child-care/hbncVisit/saveAll") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer token") @@ -137,7 +137,7 @@ void saveHBNCVisit_success() throws Exception { @Test void saveHBNCVisit_noRecordFound() throws Exception { List dtos = Collections.singletonList(new HbncRequestDTO()); - when(childCareService.saveHBNCDetails(any())).thenReturn(null); + when(childCareService.saveHBNCDetails(any(), any())).thenReturn(null); mockMvc.perform(post("/child-care/hbncVisit/saveAll") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer token") @@ -165,7 +165,7 @@ void saveHBNCVisit_invalidRequest() throws Exception { @Test void saveHBNCVisit_exception() throws Exception { List dtos = Collections.singletonList(new HbncRequestDTO()); - when(childCareService.saveHBNCDetails(any())).thenThrow(new RuntimeException("fail")); + when(childCareService.saveHBNCDetails(any(), any())).thenThrow(new RuntimeException("fail")); mockMvc.perform(post("/child-care/hbncVisit/saveAll") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer token") @@ -175,7 +175,7 @@ void saveHBNCVisit_exception() throws Exception { @Test void getHBNCVisitDetails_success() throws Exception { - when(childCareService.getHBNCDetails(any())).thenReturn(Collections.singletonList(new HbncRequestDTO())); + when(childCareService.getHBNCDetails(any())).thenReturn(Collections.singletonList(new HbncVisitResponseDTO())); GetBenRequestHandler req = new GetBenRequestHandler(); mockMvc.perform(post("/child-care/hbncVisit/getAll") .contentType(MediaType.APPLICATION_JSON) diff --git a/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java b/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java index 72e56a67a..4d42e7166 100644 --- a/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java +++ b/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java @@ -98,7 +98,7 @@ void getPregnantWomanList_noRecordFound() throws Exception { @Test void saveANCVisit_success() throws Exception { List dtos = Collections.singletonList(new ANCVisitDTO()); - when(maternalHealthService.saveANCVisit(any())).thenReturn("success"); + when(maternalHealthService.saveANCVisit(any(), any())).thenReturn("success"); mockMvc.perform(post("/maternalCare/ancVisit/saveAll") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer token") @@ -432,7 +432,7 @@ void getPregnantWomanList_exception() throws Exception { @Test void saveANCVisit_exception() throws Exception { List dtos = Collections.singletonList(new ANCVisitDTO()); - when(maternalHealthService.saveANCVisit(any())).thenThrow(new RuntimeException("fail")); + when(maternalHealthService.saveANCVisit(any(), any())).thenThrow(new RuntimeException("fail")); mockMvc.perform(post("/maternalCare/ancVisit/saveAll") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer token") diff --git a/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java index f28bb1e2f..d79df7292 100644 --- a/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java @@ -468,7 +468,7 @@ void testGetPNCVisits_success() { pncVisit.setBenId(123L); when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); - when(pncVisitRepo.getPNCForPW(userName, dto.getFromDate(), dto.getToDate())) + when(pncVisitRepo.getPNCForPW(userName)) .thenReturn(Arrays.asList(pncVisit)); // Act @@ -478,7 +478,7 @@ void testGetPNCVisits_success() { assertNotNull(result); assertEquals(1, result.size()); verify(beneficiaryRepo).getUserName(dto.getAshaId()); - verify(pncVisitRepo).getPNCForPW(userName, dto.getFromDate(), dto.getToDate()); + verify(pncVisitRepo).getPNCForPW(userName); } @Test From 18b0c3b7ecddec27e76bd0e3f867d8219f78f0a9 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Mon, 19 Oct 2026 15:16:13 +0530 Subject: [PATCH 05/24] general checkup --- .../controller/DiseaseControlController.java | 60 +++++++++++++++++ .../iemr/flw/domain/iemr/GeneralCheckUp.java | 65 +++++++++++++++++++ .../iemr/flw/dto/iemr/GeneralCheckUpDto.java | 9 +++ .../flw/dto/iemr/GeneralCheckUpListDto.java | 9 +++ 4 files changed, 143 insertions(+) create mode 100644 src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java create mode 100644 src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java create mode 100644 src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java diff --git a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java index 764ebeaf6..0c0e0f9a4 100644 --- a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java +++ b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java @@ -375,4 +375,64 @@ public ResponseEntity> getVisitDetails( } + @RequestMapping(value = "generalCheckUp/getAll", method = RequestMethod.POST) + public ResponseEntity> getGeneralCheckUp( + @RequestBody GetBenRequestHandler getBenRequestHandler) { + + Map response = new LinkedHashMap<>(); + + try { + List result = + diseaseControlService.getCdtfVisits(getBenRequestHandler); + + if (result != null && !result.isEmpty()) { + response.put("statusCode", HttpStatus.OK.value()); + response.put("data", result); + return ResponseEntity.ok(response); + } else { + response.put("statusCode", HttpStatus.NOT_FOUND.value()); + response.put("message", "No records found"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + + } catch (Exception e) { + logger.error("Error fetching Chronic Disease Visit :", e); + response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("errorMessage", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + + @RequestMapping(value = "generalCheckUp/saveAll", method = RequestMethod.POST) + public ResponseEntity> savGeneralCheckUp( + @RequestBody List requestList,@RequestHeader(value = "JwtToken") String token) { + + Map response = new LinkedHashMap<>(); + logger.info("Chronic Disease Visit Save Request: {}", requestList); + + try { + List savedList = + diseaseControlService.saveChronicDiseaseVisit(requestList,token); + + if (savedList != null && !savedList.isEmpty()) { + response.put("statusCode", HttpStatus.OK.value()); + response.put("message", "Data saved successfully"); + response.put("data", savedList); + return ResponseEntity.ok(response); + } else { + response.put("statusCode", HttpStatus.BAD_REQUEST.value()); + response.put("message", "No data saved"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + } catch (Exception e) { + logger.error("Error saving Chronic Disease Visit :", e); + response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("errorMessage", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java new file mode 100644 index 000000000..3f80c7c78 --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java @@ -0,0 +1,65 @@ +package com.iemr.flw.domain.iemr; + +import java.sql.Timestamp; +import java.time.LocalDate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +@Entity +@Table(name = "general_check_up", schema = "db_iemr") +@Data +public class GeneralCheckUp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "ben_id") + private Long benId; + + @Column(name = "household_id") + private Long householdId; + + @Column(name = "asha_id", nullable = false) + private Long ashaId; + + @Column(name = "checkup_date", nullable = false) + private LocalDate checkupDate; + + @Column(name = "problem_type", length = 200) + private String problemType; + + @Column(name = "problems", columnDefinition = "json") + private String problems; + + @Column(name = "other_text", length = 250) + private String otherText; + + @Column(name = "priority", length = 20) + private String priority; + + @Column(name = "referral_reason", length = 500) + private String referralReason; + + @Column(name = "referral_date") + private LocalDate referralDate; + + @Column(name = "created_by", length = 100) + private String createdBy; + + @Column(name = "created_date", insertable = false, updatable = false) + private Timestamp createdDate; + + @Column(name = "synced_date") + private Timestamp syncedDate; + + @Column(name = "updated_date", insertable = false, updatable = false) + private Timestamp updatedDate; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java new file mode 100644 index 000000000..a86f56201 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java @@ -0,0 +1,9 @@ +package com.iemr.flw.dto.iemr; + +public class GeneralCheckUpDto { + private Long id; + private Long beneficiaryId; + private Long houseHoldId; + private String visitDate; + private GeneralCheckUpListDto fields; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java new file mode 100644 index 000000000..a1e489bae --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java @@ -0,0 +1,9 @@ +package com.iemr.flw.dto.iemr; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class GeneralCheckUpListDto { + @JsonProperty("check_up_date") + private String CheckUpDate; + +} From 3eddc565a95a5ddc4313af7198b7a2e58aa52bd2 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 17:19:15 +0530 Subject: [PATCH 06/24] general checkup save get api --- .../flw/controller/DiseaseControlController.java | 16 +++++++++------- .../com/iemr/flw/domain/iemr/GeneralCheckUp.java | 5 ----- .../iemr/flw/service/DiseaseControlService.java | 3 +++ .../service/impl/DiseaseControlServiceImpl.java | 10 ++++++++++ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java index 0c0e0f9a4..9b23a9b90 100644 --- a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java +++ b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java @@ -377,13 +377,13 @@ public ResponseEntity> getVisitDetails( @RequestMapping(value = "generalCheckUp/getAll", method = RequestMethod.POST) public ResponseEntity> getGeneralCheckUp( - @RequestBody GetBenRequestHandler getBenRequestHandler) { + @RequestHeader(value = "JwtToken") String token) { Map response = new LinkedHashMap<>(); try { - List result = - diseaseControlService.getCdtfVisits(getBenRequestHandler); + List result = + diseaseControlService.getGeneralCheckUp(token); if (result != null && !result.isEmpty()) { response.put("statusCode", HttpStatus.OK.value()); @@ -406,14 +406,16 @@ public ResponseEntity> getGeneralCheckUp( @RequestMapping(value = "generalCheckUp/saveAll", method = RequestMethod.POST) public ResponseEntity> savGeneralCheckUp( - @RequestBody List requestList,@RequestHeader(value = "JwtToken") String token) { + @RequestBody List requestList,@RequestHeader(value = "JwtToken") String token) { Map response = new LinkedHashMap<>(); - logger.info("Chronic Disease Visit Save Request: {}", requestList); + logger.info("####################################################"); + logger.info("General checkup Disease Visit Save Request: {}", requestList); + logger.info("####################################################"); try { - List savedList = - diseaseControlService.saveChronicDiseaseVisit(requestList,token); + List savedList = + diseaseControlService.saveGeneralCheckUp(requestList,token); if (savedList != null && !savedList.isEmpty()) { response.put("statusCode", HttpStatus.OK.value()); diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java index 3f80c7c78..66390ccbc 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java @@ -39,11 +39,6 @@ public class GeneralCheckUp { @Column(name = "problems", columnDefinition = "json") private String problems; - @Column(name = "other_text", length = 250) - private String otherText; - - @Column(name = "priority", length = 20) - private String priority; @Column(name = "referral_reason", length = 500) private String referralReason; diff --git a/src/main/java/com/iemr/flw/service/DiseaseControlService.java b/src/main/java/com/iemr/flw/service/DiseaseControlService.java index 65e315dce..c7db39869 100644 --- a/src/main/java/com/iemr/flw/service/DiseaseControlService.java +++ b/src/main/java/com/iemr/flw/service/DiseaseControlService.java @@ -49,4 +49,7 @@ public interface DiseaseControlService { List saveChronicDiseaseVisit(List requestList,String token) throws IEMRException; List getCdtfVisits(GetBenRequestHandler getBenRequestHandler); + + List saveGeneralCheckUp(List generalCheckUpRequest,String token); + List getGeneralCheckUp(String token); } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index 4cbfeb874..988b6da62 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1581,4 +1581,14 @@ public List getCdtfVisits(GetBenRequestHandler getBenReq return dtoList; } + @Override + public List saveGeneralCheckUp(List generalCheckUpRequest, String token) { + + return null; + } + + @Override + public List getGeneralCheckUp(String token) { + return null; + } } \ No newline at end of file From a06bc279c93d5437e630795678972bcedc00948b Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 17:29:30 +0530 Subject: [PATCH 07/24] general checkup save get api --- .../iemr/flw/controller/DiseaseControlController.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java index 9b23a9b90..32afa2eab 100644 --- a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java +++ b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java @@ -49,6 +49,9 @@ public class DiseaseControlController { @Autowired private DiseaseControlService diseaseControlService; + @Autowired + private ObjectMapper objectMapper; + @RequestMapping(value = "malaria/saveAll", method = RequestMethod.POST, consumes = "application/json", produces = "application/json",headers = "Authorization") public ResponseEntity> saveMalaria(@RequestBody MalariaDTO malariaDTO) { @@ -410,7 +413,13 @@ public ResponseEntity> savGeneralCheckUp( Map response = new LinkedHashMap<>(); logger.info("####################################################"); - logger.info("General checkup Disease Visit Save Request: {}", requestList); + try { + logger.info("General Checkup Save Request: {}", + objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(requestList)); + } catch (Exception e) { + logger.error("Failed to convert request to JSON", e); + } logger.info("####################################################"); try { From 894da33a1e9fccf9f09d4cf4339b1c360502d668 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 17:40:47 +0530 Subject: [PATCH 08/24] general checkup save get api --- .../iemr/flw/dto/iemr/GeneralCheckUpDto.java | 3 ++ .../flw/dto/iemr/GeneralCheckUpListDto.java | 44 ++++++++++++++++++- .../com/iemr/flw/dto/iemr/ProblemDto.java | 11 +++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/iemr/flw/dto/iemr/ProblemDto.java diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java index a86f56201..da0594c3a 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpDto.java @@ -1,5 +1,8 @@ package com.iemr.flw.dto.iemr; +import lombok.Data; + +@Data public class GeneralCheckUpDto { private Long id; private Long beneficiaryId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java index a1e489bae..0cf45ab25 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java @@ -1,9 +1,51 @@ package com.iemr.flw.dto.iemr; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import java.util.List; + +@Data public class GeneralCheckUpListDto { @JsonProperty("check_up_date") - private String CheckUpDate; + private String checkUpDate; + + @JsonProperty("problem_type") + private String problemType; + + @JsonProperty("problems_g6") + private String problemsG6; + + @JsonProperty("other_text") + private String otherTextLegacy; + + private String checkupId; + + private Long ashaId; + + private String checkupDate; + + private String problemTypeId; + + private String problemTypeLabel; + + private List problems; + + private String otherText; + + private String priority; + + private Integer masterVersion; + + private String referralReason; + + private String referralDate; + + private String createdBy; + + private Long createdDate; + + private Long updatedDate; + } diff --git a/src/main/java/com/iemr/flw/dto/iemr/ProblemDto.java b/src/main/java/com/iemr/flw/dto/iemr/ProblemDto.java new file mode 100644 index 000000000..44de6eabd --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/ProblemDto.java @@ -0,0 +1,11 @@ +package com.iemr.flw.dto.iemr; + +import lombok.Data; + +@Data +public class ProblemDto { + + private String problemId; + + private String label; +} \ No newline at end of file From 82a91970c38516bf657d696e995e627f0dc412d6 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 17:58:15 +0530 Subject: [PATCH 09/24] update response --- .../iemr/flw/domain/iemr/GeneralCheckUp.java | 15 ++- .../flw/dto/iemr/GeneralCheckUpListDto.java | 2 +- .../flw/repo/iemr/GeneralCheckUpRepo.java | 12 ++ .../flw/service/DiseaseControlService.java | 2 +- .../impl/DiseaseControlServiceImpl.java | 110 +++++++++++++++++- 5 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java index 66390ccbc..f3d84c52a 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java @@ -28,7 +28,7 @@ public class GeneralCheckUp { private Long householdId; @Column(name = "asha_id", nullable = false) - private Long ashaId; + private Integer ashaId; @Column(name = "checkup_date", nullable = false) private LocalDate checkupDate; @@ -39,6 +39,19 @@ public class GeneralCheckUp { @Column(name = "problems", columnDefinition = "json") private String problems; + @Column(name = "checkup_id", length = 50, unique = true) + private String checkupId; + + @Column(name = "problem_type_label", length = 200) + private String problemTypeLabel; + + @Column(name = "other_text", length = 500) + private String otherText; + + @Column(name = "priority", length = 20) + private String priority; + + @Column(name = "referral_reason", length = 500) private String referralReason; diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java index 0cf45ab25..8091e2801 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java @@ -21,7 +21,7 @@ public class GeneralCheckUpListDto { private String checkupId; - private Long ashaId; + private Integer ashaId; private String checkupDate; diff --git a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java new file mode 100644 index 000000000..bfb09e8e2 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java @@ -0,0 +1,12 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.GeneralCheckUp; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface GeneralCheckUpRepo extends JpaRepository { + List findByCreatedByOrderByIdDesc(String userName); +} diff --git a/src/main/java/com/iemr/flw/service/DiseaseControlService.java b/src/main/java/com/iemr/flw/service/DiseaseControlService.java index c7db39869..83de6f339 100644 --- a/src/main/java/com/iemr/flw/service/DiseaseControlService.java +++ b/src/main/java/com/iemr/flw/service/DiseaseControlService.java @@ -50,6 +50,6 @@ public interface DiseaseControlService { List getCdtfVisits(GetBenRequestHandler getBenRequestHandler); - List saveGeneralCheckUp(List generalCheckUpRequest,String token); + List saveGeneralCheckUp(List generalCheckUpRequest,String token) throws IEMRException; List getGeneralCheckUp(String token); } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index 988b6da62..4a48fbedf 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -24,6 +24,8 @@ */ package com.iemr.flw.service.impl; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.iemr.flw.domain.identity.RMNCHBeneficiaryDetailsRmnch; import com.iemr.flw.domain.iemr.*; @@ -94,6 +96,15 @@ public class DiseaseControlServiceImpl implements DiseaseControlService { @Autowired private JwtUtil jwtUtil; + private static final DateTimeFormatter VISIT_DATE_FMT = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + + @Autowired + private GeneralCheckUpRepo generalCheckUpRepo; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired private IncentiveLogicService incentiveLogicService; @@ -1582,13 +1593,106 @@ public List getCdtfVisits(GetBenRequestHandler getBenReq } @Override - public List saveGeneralCheckUp(List generalCheckUpRequest, String token) { + @Transactional + public List saveGeneralCheckUp(List generalCheckUpRequest, String token) throws IEMRException { + - return null; + if (generalCheckUpRequest == null || generalCheckUpRequest.isEmpty()) { + return Collections.emptyList(); + } + + List entities = new ArrayList<>(); + + for (GeneralCheckUpDto dto : generalCheckUpRequest) { + GeneralCheckUpListDto fields = dto.getFields(); + if (fields == null) { + throw new IllegalArgumentException("fields missing for beneficiary " + dto.getBeneficiaryId()); + } + + GeneralCheckUp entity = new GeneralCheckUp(); + entity.setBenId(dto.getBeneficiaryId()); + entity.setHouseholdId(dto.getHouseHoldId()); + entity.setAshaId(jwtUtil.extractUserId(token)); + entity.setCheckupDate(LocalDate.parse(dto.getVisitDate(), VISIT_DATE_FMT)); + entity.setProblemType(fields.getProblemTypeId()); + + try { + entity.setProblems(fields.getProblems() != null + ? objectMapper.writeValueAsString(fields.getProblems()) + : null); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Invalid problems payload for checkup " + + fields.getCheckupId(), e); + } + + entity.setReferralReason(fields.getReferralReason()); + entity.setReferralDate(fields.getReferralDate() != null + ? LocalDate.parse(fields.getReferralDate()) + : null); + entity.setCreatedBy(fields.getCreatedBy()); + entity.setSyncedDate(new Timestamp(System.currentTimeMillis())); + + entities.add(entity); + } + + List saved = generalCheckUpRepo.saveAll(entities); + + for (int i = 0; i < saved.size(); i++) { + generalCheckUpRequest.get(i).setId(saved.get(i).getId()); + } + + return generalCheckUpRequest; } + @Override public List getGeneralCheckUp(String token) { - return null; + + String userName = jwtUtil.extractUsername(token); + + List rows = generalCheckUpRepo.findByCreatedByOrderByIdDesc(userName); + List result = new ArrayList<>(); + + for (GeneralCheckUp e : rows) { + String visitDate = e.getCheckupDate() != null + ? e.getCheckupDate().format(VISIT_DATE_FMT) : null; + + GeneralCheckUpListDto f = new GeneralCheckUpListDto(); + f.setCheckUpDate(visitDate); // "22-10-2026" + f.setProblemType(e.getProblemType()); // "g6" + f.setOtherText(e.getOtherText()); + f.setCheckupId(e.getCheckupId()); + f.setAshaId(e.getAshaId()); + f.setCheckupDate(e.getCheckupDate() != null ? e.getCheckupDate().toString() : null); // "2026-10-22" + f.setProblemTypeId(e.getProblemType()); + f.setProblemTypeLabel(e.getProblemTypeLabel()); + f.setOtherText(e.getOtherText()); + f.setPriority(e.getPriority()); + f.setReferralReason(e.getReferralReason()); + f.setReferralDate(e.getReferralDate() != null ? e.getReferralDate().toString() : null); + f.setCreatedBy(e.getCreatedBy()); + f.setCreatedDate(e.getCreatedDate() != null ? e.getCreatedDate().getTime() : null); + f.setUpdatedDate(e.getUpdatedDate() != null ? e.getUpdatedDate().getTime() : null); + + // json string -> List + try { + if (e.getProblems() != null) { + f.setProblems(objectMapper.readValue( + e.getProblems(), new TypeReference>() {})); + } + } catch (JsonProcessingException ex) { + throw new IllegalStateException("Invalid problems json for id " + e.getId(), ex); + } + + GeneralCheckUpDto dto = new GeneralCheckUpDto(); + dto.setId(e.getId()); + dto.setBeneficiaryId(e.getBenId()); + dto.setHouseHoldId(e.getHouseholdId()); + dto.setVisitDate(visitDate); + dto.setFields(f); + + result.add(dto); + } + return result; } } \ No newline at end of file From 2a8806c6425035f796fef2b08aa86b9bb9cfc78c Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 18:12:49 +0530 Subject: [PATCH 10/24] update response --- src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java | 2 +- .../com/iemr/flw/service/impl/DiseaseControlServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java index 8091e2801..27f58e695 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpListDto.java @@ -8,7 +8,7 @@ @Data public class GeneralCheckUpListDto { @JsonProperty("check_up_date") - private String checkUpDate; + private String check_up_date; @JsonProperty("problem_type") private String problemType; diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index 4a48fbedf..a1e999799 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1658,7 +1658,7 @@ public List getGeneralCheckUp(String token) { ? e.getCheckupDate().format(VISIT_DATE_FMT) : null; GeneralCheckUpListDto f = new GeneralCheckUpListDto(); - f.setCheckUpDate(visitDate); // "22-10-2026" + f.setCheck_up_date(visitDate); // "22-10-2026" f.setProblemType(e.getProblemType()); // "g6" f.setOtherText(e.getOtherText()); f.setCheckupId(e.getCheckupId()); From c45ec8cfd2658466d6f83643914cf6c7c91be815 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 18:27:47 +0530 Subject: [PATCH 11/24] update response --- .../flw/repo/iemr/GeneralCheckUpRepo.java | 2 +- .../impl/DiseaseControlServiceImpl.java | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java index bfb09e8e2..557f82308 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpRepo.java @@ -8,5 +8,5 @@ @Repository public interface GeneralCheckUpRepo extends JpaRepository { - List findByCreatedByOrderByIdDesc(String userName); + List findByCreatedBy(String userName); } diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index a1e999799..41365815a 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1648,12 +1648,37 @@ public List saveGeneralCheckUp(List genera @Override public List getGeneralCheckUp(String token) { - String userName = jwtUtil.extractUsername(token); + log.info("===== getGeneralCheckUp() called ====="); + log.info("Received token: {}", token); + + String userName = null; + try { + userName = jwtUtil.extractUsername(token); + log.info("Extracted username from token: {}", userName); + } catch (Exception e) { + log.error("Error while extracting username from token", e); + throw e; // ya apna custom exception throw kar sakte ho + } + + List rows = null; + try { + rows = generalCheckUpRepo.findByCreatedBy(userName); + log.info("Records fetched from DB for user [{}]: {}", userName, rows != null ? rows.size() : 0); + } catch (Exception e) { + log.error("Error while fetching GeneralCheckUp records for user: {}", userName, e); + throw e; + } - List rows = generalCheckUpRepo.findByCreatedByOrderByIdDesc(userName); List result = new ArrayList<>(); + if (rows == null || rows.isEmpty()) { + log.warn("No GeneralCheckUp records found for user: {}", userName); + return result; + } + for (GeneralCheckUp e : rows) { + log.debug("Processing GeneralCheckUp id: {}, checkupId: {}", e.getId(), e.getCheckupId()); + String visitDate = e.getCheckupDate() != null ? e.getCheckupDate().format(VISIT_DATE_FMT) : null; @@ -1679,8 +1704,12 @@ public List getGeneralCheckUp(String token) { if (e.getProblems() != null) { f.setProblems(objectMapper.readValue( e.getProblems(), new TypeReference>() {})); + log.debug("Successfully parsed problems JSON for id: {}", e.getId()); + } else { + log.debug("Problems field is null for id: {}", e.getId()); } } catch (JsonProcessingException ex) { + log.error("Invalid problems JSON for id: {}. JSON: {}", e.getId(), e.getProblems(), ex); throw new IllegalStateException("Invalid problems json for id " + e.getId(), ex); } @@ -1693,6 +1722,11 @@ public List getGeneralCheckUp(String token) { result.add(dto); } + + log.info("===== getGeneralCheckUp() completed successfully. Total records returned: {} =====", result.size()); + // Optional: full response dekhna ho to (production mein mat rakho) + // log.debug("Final response: {}", result); + return result; } } \ No newline at end of file From 3b007e30ab0f100e0afe3723aca4cb8699dd4643 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 19:54:04 +0530 Subject: [PATCH 12/24] update response --- .../java/com/iemr/flw/controller/DiseaseControlController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java index 32afa2eab..d99ce331f 100644 --- a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java +++ b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java @@ -379,8 +379,7 @@ public ResponseEntity> getVisitDetails( @RequestMapping(value = "generalCheckUp/getAll", method = RequestMethod.POST) - public ResponseEntity> getGeneralCheckUp( - @RequestHeader(value = "JwtToken") String token) { + public ResponseEntity> getGeneralCheckUp(@RequestBody GetBenRequestHandler getBenRequestHandler,@RequestHeader(value = "JwtToken") String token) { Map response = new LinkedHashMap<>(); From bd45c8723a93d6c0bbf545e02932592747df7a9f Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 19:57:12 +0530 Subject: [PATCH 13/24] update response --- .../impl/DiseaseControlServiceImpl.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index 41365815a..9989c556e 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1648,36 +1648,36 @@ public List saveGeneralCheckUp(List genera @Override public List getGeneralCheckUp(String token) { - log.info("===== getGeneralCheckUp() called ====="); - log.info("Received token: {}", token); + logger.info("===== getGeneralCheckUp() called ====="); + logger.info("Received token: {}", token); String userName = null; try { userName = jwtUtil.extractUsername(token); - log.info("Extracted username from token: {}", userName); + logger.info("Extracted username from token: {}", userName); } catch (Exception e) { - log.error("Error while extracting username from token", e); - throw e; // ya apna custom exception throw kar sakte ho + logger.error("Error while extracting username from token", e); + throw e; } List rows = null; try { rows = generalCheckUpRepo.findByCreatedBy(userName); - log.info("Records fetched from DB for user [{}]: {}", userName, rows != null ? rows.size() : 0); + logger.info("Records fetched from DB for user [{}]: {}", userName, rows != null ? rows.size() : 0); } catch (Exception e) { - log.error("Error while fetching GeneralCheckUp records for user: {}", userName, e); + logger.error("Error while fetching GeneralCheckUp records for user: {}", userName, e); throw e; } List result = new ArrayList<>(); if (rows == null || rows.isEmpty()) { - log.warn("No GeneralCheckUp records found for user: {}", userName); + logger.warn("No GeneralCheckUp records found for user: {}", userName); return result; } for (GeneralCheckUp e : rows) { - log.debug("Processing GeneralCheckUp id: {}, checkupId: {}", e.getId(), e.getCheckupId()); + logger.debug("Processing GeneralCheckUp id: {}, checkupId: {}", e.getId(), e.getCheckupId()); String visitDate = e.getCheckupDate() != null ? e.getCheckupDate().format(VISIT_DATE_FMT) : null; @@ -1704,12 +1704,12 @@ public List getGeneralCheckUp(String token) { if (e.getProblems() != null) { f.setProblems(objectMapper.readValue( e.getProblems(), new TypeReference>() {})); - log.debug("Successfully parsed problems JSON for id: {}", e.getId()); + logger.debug("Successfully parsed problems JSON for id: {}", e.getId()); } else { - log.debug("Problems field is null for id: {}", e.getId()); + logger.debug("Problems field is null for id: {}", e.getId()); } } catch (JsonProcessingException ex) { - log.error("Invalid problems JSON for id: {}. JSON: {}", e.getId(), e.getProblems(), ex); + logger.error("Invalid problems JSON for id: {}. JSON: {}", e.getId(), e.getProblems(), ex); throw new IllegalStateException("Invalid problems json for id " + e.getId(), ex); } @@ -1723,9 +1723,8 @@ public List getGeneralCheckUp(String token) { result.add(dto); } - log.info("===== getGeneralCheckUp() completed successfully. Total records returned: {} =====", result.size()); - // Optional: full response dekhna ho to (production mein mat rakho) - // log.debug("Final response: {}", result); + logger.info("===== getGeneralCheckUp() completed successfully. Total records returned: {} =====", result.size()); + return result; } From 516f75b93a99df1b2d60f70ce2c58f43a6610409 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 20:08:43 +0530 Subject: [PATCH 14/24] update response --- .../iemr/flw/service/impl/DiseaseControlServiceImpl.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index 9989c556e..6a44aeedf 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1651,18 +1651,17 @@ public List getGeneralCheckUp(String token) { logger.info("===== getGeneralCheckUp() called ====="); logger.info("Received token: {}", token); - String userName = null; + Integer userName = null; try { - userName = jwtUtil.extractUsername(token); + userName = jwtUtil.extractUserId(token); logger.info("Extracted username from token: {}", userName); } catch (Exception e) { logger.error("Error while extracting username from token", e); - throw e; } List rows = null; try { - rows = generalCheckUpRepo.findByCreatedBy(userName); + rows = generalCheckUpRepo.findByCreatedBy(userRepo.getUserNamedByUserId(userName)); logger.info("Records fetched from DB for user [{}]: {}", userName, rows != null ? rows.size() : 0); } catch (Exception e) { logger.error("Error while fetching GeneralCheckUp records for user: {}", userName, e); From fc62b950ff3396255c2681200c06f5bb78c809aa Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 20:27:36 +0530 Subject: [PATCH 15/24] update response --- .../com/iemr/flw/service/impl/DiseaseControlServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index 6a44aeedf..cb0457764 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1685,7 +1685,7 @@ public List getGeneralCheckUp(String token) { f.setCheck_up_date(visitDate); // "22-10-2026" f.setProblemType(e.getProblemType()); // "g6" f.setOtherText(e.getOtherText()); - f.setCheckupId(e.getCheckupId()); + f.setCheckupId(e.getId().toString()); f.setAshaId(e.getAshaId()); f.setCheckupDate(e.getCheckupDate() != null ? e.getCheckupDate().toString() : null); // "2026-10-22" f.setProblemTypeId(e.getProblemType()); From 8a665b9758ed496fcd41de9ef29bfd3ab2998ac2 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 20 Oct 2026 20:40:47 +0530 Subject: [PATCH 16/24] update response --- .../iemr/flw/service/impl/DiseaseControlServiceImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index cb0457764..916953a36 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1615,6 +1615,9 @@ public List saveGeneralCheckUp(List genera entity.setAshaId(jwtUtil.extractUserId(token)); entity.setCheckupDate(LocalDate.parse(dto.getVisitDate(), VISIT_DATE_FMT)); entity.setProblemType(fields.getProblemTypeId()); + entity.setProblemTypeLabel(fields.getProblemTypeLabel()); + entity.setPriority(fields.getPriority()); + entity.setOtherText(fields.getOtherText()); try { entity.setProblems(fields.getProblems() != null @@ -1682,8 +1685,8 @@ public List getGeneralCheckUp(String token) { ? e.getCheckupDate().format(VISIT_DATE_FMT) : null; GeneralCheckUpListDto f = new GeneralCheckUpListDto(); - f.setCheck_up_date(visitDate); // "22-10-2026" - f.setProblemType(e.getProblemType()); // "g6" + f.setCheck_up_date(visitDate); + f.setProblemType(e.getProblemType()); f.setOtherText(e.getOtherText()); f.setCheckupId(e.getId().toString()); f.setAshaId(e.getAshaId()); From b2c8705b03653996c786ed18355423937e04cf74 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 27 Oct 2026 17:20:56 +0530 Subject: [PATCH 17/24] fix image store issue in village level froms --- src/main/java/com/iemr/flw/domain/iemr/AHDForm.java | 6 ++++-- .../java/com/iemr/flw/domain/iemr/DewormingForm.java | 6 ++++-- .../java/com/iemr/flw/domain/iemr/PHCReviewForm.java | 6 ++++-- src/main/java/com/iemr/flw/domain/iemr/VHNDForm.java | 6 ++++-- src/main/java/com/iemr/flw/domain/iemr/VhncForm.java | 10 ++++++---- .../java/com/iemr/flw/dto/iemr/AhDMeetingFormDTO.java | 4 ++-- .../java/com/iemr/flw/dto/iemr/DewormingFormDTO.java | 4 ++-- .../com/iemr/flw/dto/iemr/PhcReviewMeetingFormDTO.java | 4 ++-- src/main/java/com/iemr/flw/dto/iemr/VHNDFormDTO.java | 4 ++-- src/main/java/com/iemr/flw/dto/iemr/VhncFormDTO.java | 4 ++-- .../flw/service/impl/VillageLevelFormServiceImpl.java | 4 ++-- 11 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/iemr/flw/domain/iemr/AHDForm.java b/src/main/java/com/iemr/flw/domain/iemr/AHDForm.java index 808ba7827..f5565b3d6 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/AHDForm.java +++ b/src/main/java/com/iemr/flw/domain/iemr/AHDForm.java @@ -26,11 +26,13 @@ public class AHDForm { @Column(name = "ahd_date") private String ahdDate; + @Lob @Column(name = "image1") - private String image1; + private byte[] image1; + @Lob @Column(name = "image2") - private String image2; + private byte[] image2; @Column(name = "user_id") private Long userId; diff --git a/src/main/java/com/iemr/flw/domain/iemr/DewormingForm.java b/src/main/java/com/iemr/flw/domain/iemr/DewormingForm.java index 1638e52e5..3df5156b2 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DewormingForm.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DewormingForm.java @@ -29,11 +29,13 @@ public class DewormingForm { @Column(name = "age_group") private Integer ageGroup; + @Lob @Column(name = "image1") - private String image1; + private byte[] image1; + @Lob @Column(name = "image2") - private String image2; + private byte[] image2; @Column(name = "user_id") private Long userId; diff --git a/src/main/java/com/iemr/flw/domain/iemr/PHCReviewForm.java b/src/main/java/com/iemr/flw/domain/iemr/PHCReviewForm.java index 42665c94c..9670f08f1 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PHCReviewForm.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PHCReviewForm.java @@ -26,11 +26,13 @@ public class PHCReviewForm { @Column(name = "no_of_beneficiaries_attended") private Integer noOfBeneficiariesAttended; + @Lob @Column(name = "image1") - private String image1; + private byte[] image1; + @Lob @Column(name = "image2") - private String image2; + private byte[] image2; @Column(name = "user_id") private Long userId; diff --git a/src/main/java/com/iemr/flw/domain/iemr/VHNDForm.java b/src/main/java/com/iemr/flw/domain/iemr/VHNDForm.java index 2de5f6062..c940e8ea3 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/VHNDForm.java +++ b/src/main/java/com/iemr/flw/domain/iemr/VHNDForm.java @@ -28,11 +28,13 @@ public class VHNDForm { @Column(name = "no_of_beneficiaries_attended") private Integer noOfBeneficiariesAttended; + @Lob @Column(name = "image1") - private String image1; + private byte[] image1; + @Lob @Column(name = "image2") - private String image2; + private byte[] image2; @Column(name = "form_type") private String formType; diff --git a/src/main/java/com/iemr/flw/domain/iemr/VhncForm.java b/src/main/java/com/iemr/flw/domain/iemr/VhncForm.java index 858554daf..25bec9354 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/VhncForm.java +++ b/src/main/java/com/iemr/flw/domain/iemr/VhncForm.java @@ -25,12 +25,14 @@ public class VhncForm { @Column(name = "no_of_beneficiaries_attended") private Integer noOfBeneficiariesAttended; - + + @Lob @Column(name = "image1") - private String image1; - + private byte[] image1; + + @Lob @Column(name = "image2") - private String image2; + private byte[] image2; @Column(name = "user_id") private Long userId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/AhDMeetingFormDTO.java b/src/main/java/com/iemr/flw/dto/iemr/AhDMeetingFormDTO.java index 0ecc31be8..fdb6b9bbf 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/AhDMeetingFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/AhDMeetingFormDTO.java @@ -8,6 +8,6 @@ public class AhDMeetingFormDTO { private String mobilizedForAHD; private String ahdPlace; private String ahdDate; - private String image1; - private String image2; + private byte[] image1; + private byte[] image2; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/iemr/DewormingFormDTO.java b/src/main/java/com/iemr/flw/dto/iemr/DewormingFormDTO.java index 3c3feb613..f7d6fa2bb 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/DewormingFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/DewormingFormDTO.java @@ -12,6 +12,6 @@ public class DewormingFormDTO { private String dewormingDate; private String dewormingLocation; private Integer ageGroup; - private String image1; - private String image2; + private byte[] image1; + private byte[] image2; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/iemr/PhcReviewMeetingFormDTO.java b/src/main/java/com/iemr/flw/dto/iemr/PhcReviewMeetingFormDTO.java index b08386c6e..4a88d0bd4 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/PhcReviewMeetingFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/PhcReviewMeetingFormDTO.java @@ -13,8 +13,8 @@ public class PhcReviewMeetingFormDTO { private String mitaninHistory; private String mitaninActivityCheckList; private Integer placeId; - private String image1; - private String image2; + private byte[] image1; + private byte[] image2; diff --git a/src/main/java/com/iemr/flw/dto/iemr/VHNDFormDTO.java b/src/main/java/com/iemr/flw/dto/iemr/VHNDFormDTO.java index aa99186a1..3ab437857 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/VHNDFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/VHNDFormDTO.java @@ -35,8 +35,8 @@ public class VHNDFormDTO { private String vhndDate; private String place; private Integer noOfBeneficiariesAttended; - private String image1; - private String image2; + private byte[] image1; + private byte[] image2; private Integer vhndPlaceId; private String pregnantWomenAnc; private String lactatingMothersPnc; diff --git a/src/main/java/com/iemr/flw/dto/iemr/VhncFormDTO.java b/src/main/java/com/iemr/flw/dto/iemr/VhncFormDTO.java index a2515b421..45f159689 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/VhncFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/VhncFormDTO.java @@ -32,8 +32,8 @@ public class VhncFormDTO { private String vhncDate; private String place; private Integer noOfBeneficiariesAttended; - private String image1; - private String image2; + private byte[] image1; + private byte[] image2; private String villageName; private Integer anm; private Integer aww; diff --git a/src/main/java/com/iemr/flw/service/impl/VillageLevelFormServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/VillageLevelFormServiceImpl.java index ff1482e08..52a33bc0a 100644 --- a/src/main/java/com/iemr/flw/service/impl/VillageLevelFormServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/VillageLevelFormServiceImpl.java @@ -233,8 +233,8 @@ private Boolean saveVhndFormData(VHNDFormDTO vhndFormDTO, Integer userID) { VHNDForm vhndForm = new VHNDForm(); vhndForm.setUserId(userID); vhndForm.setVhndDate(vhndFormDTO.getVhndDate()); - vhndForm.setImage2(vhndFormDTO.getImage2() != null ? vhndFormDTO.getImage2() : ""); - vhndForm.setImage1(vhndFormDTO.getImage1() != null ? vhndFormDTO.getImage1() : ""); + vhndForm.setImage2(vhndFormDTO.getImage2() != null ? vhndFormDTO.getImage2() : null); + vhndForm.setImage1(vhndFormDTO.getImage1() != null ? vhndFormDTO.getImage1() : null); vhndForm.setPlace(vhndFormDTO.getPlace() != null ? vhndFormDTO.getPlace() : ""); vhndForm.setNoOfBeneficiariesAttended( From 0f66c1a4256e06f2516f0b1afb7f0b07cb912e76 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Tue, 27 Oct 2026 17:24:36 +0530 Subject: [PATCH 18/24] fix image store issue in village level froms --- src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java index c6bf2695a..ae7dc52db 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java @@ -53,7 +53,7 @@ public class DiagnosticOrder { @Column(name = "error_message", columnDefinition = "TEXT") private String errorMessage; - @Column(name = "reason_for_refusal", columnDefinition = "TEXT") + @Column(name = "reason_to_close", columnDefinition = "TEXT") private String reasonToClose; @Column(name = "push_response_json", columnDefinition = "LONGTEXT") From 32855a3dcff93df36e562f846da0d07170881790 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 28 Oct 2026 17:08:04 +0530 Subject: [PATCH 19/24] Add api for general checkup follow up --- .../controller/DiseaseControlController.java | 66 ++ .../domain/iemr/GeneralCheckUpFollowUp.java | 48 ++ .../dto/iemr/GeneralCheckUpFollowUpDto.java | 12 + .../iemr/GeneralCheckUpFollowUpListDto.java | 19 + .../flw/repo/iemr/BenReferDetailsRepo.java | 3 + .../flw/repo/iemr/CbacIemrDetailsRepo.java | 3 + .../repo/iemr/EligibleCoupleRegisterRepo.java | 2 + .../GeneralCheckUpFollowUpRepository.java | 10 + .../iemr/IFAFormSubmissionRepository.java | 2 + .../flw/repo/iemr/IncentiveRecordRepo.java | 15 + .../iemr/flw/repo/iemr/IncentivesRepo.java | 3 + .../flw/service/DiseaseControlService.java | 2 + .../impl/DiseaseControlServiceImpl.java | 155 +++- .../service/impl/IncentiveServiceImpl.java | 792 +++++++++--------- 14 files changed, 735 insertions(+), 397 deletions(-) create mode 100644 src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java create mode 100644 src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java create mode 100644 src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpListDto.java create mode 100644 src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java diff --git a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java index d99ce331f..318b29bad 100644 --- a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java +++ b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java @@ -445,4 +445,70 @@ public ResponseEntity> savGeneralCheckUp( } + @RequestMapping(value = "generalCheckUpFollowUp/getAll", method = RequestMethod.POST) + public ResponseEntity> getGeneralCheckUpFollowUp(@RequestBody GetBenRequestHandler getBenRequestHandler,@RequestHeader(value = "JwtToken") String token) { + + Map response = new LinkedHashMap<>(); + + try { + List result = + diseaseControlService.getGeneralCheckUpFollowUp(token); + + if (result != null && !result.isEmpty()) { + response.put("statusCode", HttpStatus.OK.value()); + response.put("data", result); + return ResponseEntity.ok(response); + } else { + response.put("statusCode", HttpStatus.NOT_FOUND.value()); + response.put("message", "No records found"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + + } catch (Exception e) { + logger.error("Error fetching Chronic Disease Visit :", e); + response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("errorMessage", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + + @RequestMapping(value = "generalCheckUpFollowUp/saveAll", method = RequestMethod.POST) + public ResponseEntity> savGeneralCheckUpFollowUp( + @RequestBody List requestList,@RequestHeader(value = "JwtToken") String token) { + + Map response = new LinkedHashMap<>(); + logger.info("####################################################"); + try { + logger.info("General Checkup Save Request: {}", + objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(requestList)); + } catch (Exception e) { + logger.error("Failed to convert request to JSON", e); + } + logger.info("####################################################"); + + try { + List savedList = + diseaseControlService.saveGeneralCheckUpFollowUp(requestList,token); + + if (savedList != null && !savedList.isEmpty()) { + response.put("statusCode", HttpStatus.OK.value()); + response.put("message", "Data saved successfully"); + response.put("data", savedList); + return ResponseEntity.ok(response); + } else { + response.put("statusCode", HttpStatus.BAD_REQUEST.value()); + response.put("message", "No data saved"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + } catch (Exception e) { + logger.error("Error saving Chronic Disease Visit :", e); + response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("errorMessage", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java new file mode 100644 index 000000000..f907e377a --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java @@ -0,0 +1,48 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.*; +import lombok.Data; + +import java.sql.Timestamp; +import java.time.LocalDate; + +@Entity +@Table(name = "general_checkup_followup", schema = "db_iemr") +@Data +public class GeneralCheckUpFollowUp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "ben_id") + private Long benId; + + @Column(name = "household_id") + private Long householdId; + + @Column(name = "asha_id", nullable = false) + private Integer ashaId; + + @Column(name = "follow_up_date", nullable = false) + private LocalDate followUpDate; + + @Column(name = "followup_status", length = 200) + private String followupStatus; + + @Column(name = "referred_to", columnDefinition = "json") + private String referredTo; + + @Column(name = "created_by", length = 100) + private String createdBy; + + @Column(name = "created_date", insertable = false, updatable = false) + private Timestamp createdDate; + + @Column(name = "synced_date") + private Timestamp syncedDate; + + @Column(name = "updated_date", insertable = false, updatable = false) + private Timestamp updatedDate; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java new file mode 100644 index 000000000..eebd3b24e --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java @@ -0,0 +1,12 @@ +package com.iemr.flw.dto.iemr; + +import lombok.Data; + +@Data +public class GeneralCheckUpFollowUpDto { + private Long id; + private Long beneficiaryId; + private Long houseHoldId; + private String visitDate; + private GeneralCheckUpFollowUpListDto fields; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpListDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpListDto.java new file mode 100644 index 000000000..fc8bdd588 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpListDto.java @@ -0,0 +1,19 @@ +package com.iemr.flw.dto.iemr; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class GeneralCheckUpFollowUpListDto { + @JsonProperty("follow_up_date") + private String followUpDate; + + @JsonProperty("followup_status") + private String followupStatus; + + @JsonProperty("referred_to") + private String referredTo; + +} diff --git a/src/main/java/com/iemr/flw/repo/iemr/BenReferDetailsRepo.java b/src/main/java/com/iemr/flw/repo/iemr/BenReferDetailsRepo.java index 25a0b652a..dbb76975e 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/BenReferDetailsRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/BenReferDetailsRepo.java @@ -47,6 +47,9 @@ public interface BenReferDetailsRepo extends JpaRepository findByCreatedBy(String userName); + @Query(value = "SELECT COUNT(*) FROM db_iemr.t_benreferdetails WHERE CreatedBy = :userName AND revisitDate >= :startDate AND revisitDate < :endDate", nativeQuery = true) + Long countMonthlyReferralsByDateRange(@Param("userName") String userName, @Param("startDate") java.sql.Timestamp startDate, @Param("endDate") java.sql.Timestamp endDate); + @Query(value = """ SELECT COUNT(*) FROM db_iemr.t_benreferdetails diff --git a/src/main/java/com/iemr/flw/repo/iemr/CbacIemrDetailsRepo.java b/src/main/java/com/iemr/flw/repo/iemr/CbacIemrDetailsRepo.java index 5e93dfad6..82a96c807 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/CbacIemrDetailsRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/CbacIemrDetailsRepo.java @@ -37,6 +37,9 @@ public interface CbacIemrDetailsRepo extends CrudRepository findByCreatedBy(String userName); + @Query("SELECT c.beneficiaryRegId, c.createdDate FROM CbacDetailsImer c WHERE c.createdBy = :userName AND c.beneficiaryRegId IS NOT NULL AND c.createdDate IS NOT NULL") + List findBenRegIdAndCreatedDateByCreatedBy(@Param("userName") String userName); + @Query(value = "SELECT BeneficiaryID FROM db_identity.m_beneficiaryregidmapping " + "WHERE BenRegId = :benRegId AND Deleted = 0", nativeQuery = true) Long getBeneficiaryId(@Param("benRegId") Long benRegId); diff --git a/src/main/java/com/iemr/flw/repo/iemr/EligibleCoupleRegisterRepo.java b/src/main/java/com/iemr/flw/repo/iemr/EligibleCoupleRegisterRepo.java index 218c5b2b3..d9418df08 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/EligibleCoupleRegisterRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/EligibleCoupleRegisterRepo.java @@ -16,6 +16,8 @@ public interface EligibleCoupleRegisterRepo extends JpaRepository findByCreatedBy(String userName); + long countByCreatedBy(String userName); + @Query(" SELECT ecr FROM EligibleCoupleRegister ecr WHERE ecr.createdBy = :userId and ecr.createdDate >= :fromDate and ecr.createdDate <= :toDate") List getECRegRecords(@Param("userId") String userId, @Param("fromDate") Timestamp fromDate, @Param("toDate") Timestamp toDate); diff --git a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java new file mode 100644 index 000000000..1b6f0d4b7 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java @@ -0,0 +1,10 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.GeneralCheckUpFollowUp; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface GeneralCheckUpFollowUpRepository + extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/repo/iemr/IFAFormSubmissionRepository.java b/src/main/java/com/iemr/flw/repo/iemr/IFAFormSubmissionRepository.java index 0b1b5ff72..88201f45b 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/IFAFormSubmissionRepository.java +++ b/src/main/java/com/iemr/flw/repo/iemr/IFAFormSubmissionRepository.java @@ -11,6 +11,8 @@ public interface IFAFormSubmissionRepository extends JpaRepository { List findByUserId(Integer userName); + + long countByUserId(Integer userId); @Query("SELECT i FROM IFAFormSubmissionData i " + "WHERE i.userId = :userId " + "AND i.visitDate = :visitDate") diff --git a/src/main/java/com/iemr/flw/repo/iemr/IncentiveRecordRepo.java b/src/main/java/com/iemr/flw/repo/iemr/IncentiveRecordRepo.java index 74acfa5f8..d72c069d6 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/IncentiveRecordRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/IncentiveRecordRepo.java @@ -38,6 +38,21 @@ IncentiveActivityRecord findRecordByActivityIdCreatedDateBenId( Optional findById(Long id); + @Query("SELECT record FROM IncentiveActivityRecord record WHERE record.ashaId = :ashaId ORDER BY record.id DESC") + List findRecordsByAshaOrderByIdDesc(@Param("ashaId") Integer ashaId); + + @Query("SELECT r.activityId FROM IncentiveActivityRecord r WHERE r.activityId IN :activityIds AND r.benId = :benId AND r.ashaId = :ashaId AND r.createdDate >= :startOfMonth AND r.createdDate < :startOfNextMonth") + List findExistingActivityIdsByMonth(@Param("activityIds") List activityIds, @Param("benId") Long benId, @Param("ashaId") Integer ashaId, @Param("startOfMonth") Timestamp startOfMonth, @Param("startOfNextMonth") Timestamp startOfNextMonth); + + @Query("SELECT r.benId, r.startDate FROM IncentiveActivityRecord r WHERE r.activityId = :activityId AND r.ashaId = :ashaId") + List findBenIdAndStartDateByActivityIdAndAshaId(@Param("activityId") Long activityId, @Param("ashaId") Integer ashaId); + + @Query("SELECT r FROM IncentiveActivityRecord r WHERE r.ashaId = :ashaId AND r.isClaimed = true AND r.createdDate >= :startDate AND r.createdDate < :endDate") + List findClaimedRecordsByAshaAndCreatedDateRange(@Param("ashaId") Integer ashaId, @Param("startDate") Timestamp startDate, @Param("endDate") Timestamp endDate); + + @Query("SELECT r FROM IncentiveActivityRecord r WHERE r.ashaId = :ashaId AND r.activityId = :activityId AND r.createdDate >= :startDate AND r.createdDate < :endDate") + List findRecordsByAshaAndActivityAndCreatedDateRange(@Param("ashaId") Integer ashaId, @Param("activityId") Long activityId, @Param("startDate") Timestamp startDate, @Param("endDate") Timestamp endDate); + @Query(value = """ SELECT * diff --git a/src/main/java/com/iemr/flw/repo/iemr/IncentivesRepo.java b/src/main/java/com/iemr/flw/repo/iemr/IncentivesRepo.java index d1cbc7f84..10faf62f5 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/IncentivesRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/IncentivesRepo.java @@ -45,6 +45,9 @@ IncentiveActivity findIncentiveMasterById( List findByGroupNotAndIsDeleted(String group, Boolean isDeleted); + @Query("SELECT i FROM IncentiveActivity i WHERE i.name IN :names AND i.isDeleted = false") + List findByNamesAndIsDeletedFalse(@Param("names") List names); + // IncentivesRepo — replaces N calls to findIncentiveMasterById() @Query("SELECT i.id FROM IncentiveActivity i WHERE i.id IN :ids AND i.isDeleted = false AND " + "(:isCG = true AND i.group = 'ACTIVITY' OR :isCG = false AND i.group != 'ACTIVITY')") diff --git a/src/main/java/com/iemr/flw/service/DiseaseControlService.java b/src/main/java/com/iemr/flw/service/DiseaseControlService.java index 83de6f339..363dcb5d2 100644 --- a/src/main/java/com/iemr/flw/service/DiseaseControlService.java +++ b/src/main/java/com/iemr/flw/service/DiseaseControlService.java @@ -51,5 +51,7 @@ public interface DiseaseControlService { List getCdtfVisits(GetBenRequestHandler getBenRequestHandler); List saveGeneralCheckUp(List generalCheckUpRequest,String token) throws IEMRException; + List saveGeneralCheckUpFollowUp(List generalCheckUpFollowUpDtos,String token) throws IEMRException; List getGeneralCheckUp(String token); + List getGeneralCheckUpFollowUp(String token); } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index 916953a36..a49527d3a 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -113,8 +113,9 @@ public class DiseaseControlServiceImpl implements DiseaseControlService { @Autowired private BeneficiaryRepo beneficiaryRepo; - - +@Autowired + private GeneralCheckUpFollowUpRepository + generalCheckUpFollowUpRepository; private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); @Override @@ -1647,6 +1648,156 @@ public List saveGeneralCheckUp(List genera return generalCheckUpRequest; } + @Override + @Transactional + public List saveGeneralCheckUpFollowUp( + List dtos, + String token) throws IEMRException { + + if (dtos == null || dtos.isEmpty()) { + return Collections.emptyList(); + } + + try { + Integer userId = jwtUtil.extractUserId(token); + String username = jwtUtil.extractUsername(token); + + List entities = new ArrayList<>(); + + for (GeneralCheckUpFollowUpDto dto : dtos) { + + if (dto == null || dto.getFields() == null) { + throw new IllegalArgumentException( + "Follow-up details are required"); + } + + GeneralCheckUpFollowUpListDto fields = dto.getFields(); + + String date = fields.getFollowUpDate(); + + if (date == null || date.isBlank()) { + date = dto.getVisitDate(); + } + + if (date == null || date.isBlank()) { + throw new IllegalArgumentException( + "Follow-up date is required"); + } + + LocalDate followUpDate = LocalDate.parse( + date.substring(0, 10)); + + String status = fields.getFollowupStatus(); + + if (status == null || status.isBlank()) { + throw new IllegalArgumentException( + "Follow-up status is required"); + } + + boolean referredToHigherFacility = + "referred_to_higher_facility".equalsIgnoreCase(status); + + if (referredToHigherFacility + && (fields.getReferredTo() == null + || fields.getReferredTo().isBlank())) { + throw new IllegalArgumentException( + "Referred-to facility is required"); + } + + GeneralCheckUpFollowUp entity = + new GeneralCheckUpFollowUp(); + + entity.setBenId(dto.getBeneficiaryId()); + entity.setHouseholdId(dto.getHouseHoldId()); + entity.setFollowUpDate(followUpDate); + entity.setFollowupStatus(status); + + entity.setReferredTo( + referredToHigherFacility + ? fields.getReferredTo() + : null + ); + + entity.setAshaId(userId); + entity.setCreatedBy(username); + + entities.add(entity); + } + + List savedEntities = + generalCheckUpFollowUpRepository.saveAll(entities); + + return savedEntities.stream() + .map(entity -> { + GeneralCheckUpFollowUpDto dto = + new GeneralCheckUpFollowUpDto(); + + dto.setId(entity.getId()); + dto.setBeneficiaryId(entity.getBenId()); + dto.setHouseHoldId(entity.getHouseholdId()); + + GeneralCheckUpFollowUpListDto fields = + new GeneralCheckUpFollowUpListDto(); + + fields.setFollowUpDate( + entity.getFollowUpDate().toString()); + fields.setFollowupStatus( + entity.getFollowupStatus()); + fields.setReferredTo(entity.getReferredTo()); + + dto.setFields(fields); + return dto; + }) + .collect(Collectors.toList()); + + } catch (Exception e) { + // Replace with your project's standard IEMRException constructor. + throw new RuntimeException( + "Error saving General Check-up follow-up", e); + } + } + + @Override + @Transactional(readOnly = true) + public List getGeneralCheckUpFollowUp( + String token) { + + Integer userId = getUserIdFromToken(token); + + List entities = + generalCheckUpFollowUpRepository.findByAshaId(userId); + + return entities.stream() + .map(entity -> { + GeneralCheckUpFollowUpDto dto = + new GeneralCheckUpFollowUpDto(); + + dto.setId(entity.getId()); + dto.setBeneficiaryId(entity.getBenId()); + dto.setHouseHoldId(entity.getHouseholdId()); + + GeneralCheckUpFollowUpListDto fields = + new GeneralCheckUpFollowUpListDto(); + + fields.setFollowUpDate( + entity.getFollowUpDate() != null + ? entity.getFollowUpDate().toString() + : null); + + fields.setFollowupStatus(entity.getFollowupStatus()); + fields.setReferredTo(entity.getReferredTo()); + + dto.setFields(fields); + + dto.setVisitDate( + entity.getFollowUpDate() != null + ? entity.getFollowUpDate().toString() + : null); + + return dto; + }) + .collect(Collectors.toList()); + } @Override public List getGeneralCheckUp(String token) { diff --git a/src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java index b89cb137f..7fa1ef08a 100644 --- a/src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java @@ -199,197 +199,198 @@ public String getIncentiveMaster(IncentiveRequestDTO incentiveRequestDTO) { } return null; } + @Override public String getAllIncentivesByUserId(GetBenRequestHandler request) { - int page = 0; - int size = 200; // bumped from 20 — fewer round trips for large histories - Page pageResult; - List finalDtos = new ArrayList<>(); + Integer ashaId = request.getAshaId(); - // Fetch user detail ONCE instead of twice - UserServiceRoleDTO userDetail = userService.getUserDetail(request.getAshaId()); - Integer stateCode = userDetail.getStateId(); - String userName = userDetail.getUserName(); + // Fetch user stateId and userName in ONE lightweight query instead of heavy getUserDetail() (which builds facilityData) + List userRoles = userRepo.getUserRole(ashaId); + UserServiceRoleDTO userDetail = (userRoles != null && !userRoles.isEmpty()) + ? userRoles.get(0) + : userService.getUserDetail(ashaId); + Integer stateCode = userDetail != null ? userDetail.getStateId() : null; + String userName = userDetail != null ? userDetail.getUserName() : null; try { - if (stateCode.equals(StateCode.AM.getStateCode())) { - checkMonthlyAshaIncentive(request.getAshaId()); - } - if (stateCode.equals(StateCode.CG.getStateCode())) { - checkMonthlyAshaIncentiveForCg(request.getAshaId()); + if (stateCode != null && stateCode.equals(StateCode.AM.getStateCode())) { + checkMonthlyAshaIncentive(ashaId, userName); + } else if (stateCode != null && stateCode.equals(StateCode.CG.getStateCode())) { + checkMonthlyAshaIncentiveForCg(ashaId, userName); } } catch (Exception e) { logger.error("Error in checkMonthlyAshaIncentive: ", e); } try { - addIncentiveForIronTablets(request.getAshaId()); - incentiveOfNcdReferal(request.getAshaId(), request.getVillageID(),request); + addIncentiveForIronTablets(ashaId, userName, stateCode); + Integer effectiveStateId = request.getVillageID() != null ? request.getVillageID() : stateCode; + incentiveOfNcdReferal(ashaId, effectiveStateId, request, userName); } catch (Exception e) { logger.error("Error in incentiveOfNcdReferal: ", e); } boolean isCG = stateCode != null && stateCode.equals(StateCode.CG.getStateCode()); - do { - Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending()); + // Fetch all records for this ASHA in a single query ordered by id DESC (avoids Page COUNT(DISTINCT) + multi-page round trips) + List entities = recordRepo.findRecordsByAshaOrderByIdDesc(ashaId); - pageResult = recordRepo.findRecordsByAsha(request.getAshaId(), pageable); + if (entities.isEmpty()) { + return new Gson().toJson(Collections.emptyList()); + } - List entities = pageResult.getContent(); + // Bulk fetch valid activity ids + List activityIds = entities.stream() + .map(IncentiveActivityRecord::getActivityId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); - if (entities.isEmpty()) { - return new Gson().toJson(Collections.emptyList()); - } + if (activityIds.isEmpty()) { + return new Gson().toJson(Collections.emptyList()); + } - // Bulk fetch valid activity ids - List activityIds = entities.stream() - .map(IncentiveActivityRecord::getActivityId) - .distinct() - .collect(Collectors.toList()); + Set validActivityIds = isCG + ? incentivesRepo.findValidActivityIds(activityIds, true) + : incentivesRepo.findValidActivityIds(activityIds, false); - Set validActivityIds = isCG - ? incentivesRepo.findValidActivityIds(activityIds, true) - : incentivesRepo.findValidActivityIds(activityIds, false); + entities = entities.stream() + .filter(e -> validActivityIds.contains(e.getActivityId())) + .collect(Collectors.toList()); - entities = entities.stream() - .filter(e -> validActivityIds.contains(e.getActivityId())) - .collect(Collectors.toList()); + if (entities.isEmpty()) { + return new Gson().toJson(Collections.emptyList()); + } - // Bulk fetch beneficiary names - List benIdsToFetch = entities.stream() - .filter(e -> e.getName() == null && e.getBenId() != null && e.getBenId() > 0) - .map(IncentiveActivityRecord::getBenId) - .distinct() - .collect(Collectors.toList()); + // Bulk fetch beneficiary names + List benIdsToFetch = entities.stream() + .filter(e -> e.getName() == null && e.getBenId() != null && e.getBenId() > 0) + .map(IncentiveActivityRecord::getBenId) + .distinct() + .collect(Collectors.toList()); - Map benIdToNameMap = new HashMap<>(); - Map regIdToBenIdMap = new HashMap<>(); + Map benIdToNameMap = new HashMap<>(); + Map regIdToBenIdMap = new HashMap<>(); - if (benIdsToFetch != null && !benIdsToFetch.isEmpty()) { + if (!benIdsToFetch.isEmpty()) { - // First, fetch names using BeneficiaryId - List benDetails = - beneficiaryRepo.findBenNamesByBenIds(benIdsToFetch); + // First, fetch names using BeneficiaryId + List benDetails = + beneficiaryRepo.findBenNamesByBenIds(benIdsToFetch); - Set foundBenIds = new HashSet<>(); + Set foundBenIds = new HashSet<>(); - for (Object[] row : benDetails) { - Long benId = ((Number) row[0]).longValue(); + for (Object[] row : benDetails) { + Long benId = ((Number) row[0]).longValue(); - String first = row[1] != null ? row[1].toString() : ""; - String last = row[2] != null ? row[2].toString() : ""; + String first = row[1] != null ? row[1].toString() : ""; + String last = row[2] != null ? row[2].toString() : ""; - benIdToNameMap.put(benId, (first + " " + last).trim()); - foundBenIds.add(benId); - } + benIdToNameMap.put(benId, (first + " " + last).trim()); + foundBenIds.add(benId); + } - // IDs not found as BeneficiaryId - List unresolvedIds = benIdsToFetch.stream() - .filter(Objects::nonNull) - .filter(id -> !foundBenIds.contains(id)) - .distinct() - .toList(); + // IDs not found as BeneficiaryId + List unresolvedIds = benIdsToFetch.stream() + .filter(Objects::nonNull) + .filter(id -> !foundBenIds.contains(id)) + .distinct() + .toList(); - if (!unresolvedIds.isEmpty()) { + if (!unresolvedIds.isEmpty()) { - // Resolve BenRegId -> BeneficiaryId - List mappings = - beneficiaryRepo.getBenIdsFromRegIds(unresolvedIds); + // Resolve BenRegId -> BeneficiaryId + List mappings = + beneficiaryRepo.getBenIdsFromRegIds(unresolvedIds); - Set resolvedBenIds = new HashSet<>(); + Set resolvedBenIds = new HashSet<>(); - for (Object[] row : mappings) { - Long regId = ((Number) row[0]).longValue(); - Long benId = ((Number) row[1]).longValue(); + for (Object[] row : mappings) { + Long regId = ((Number) row[0]).longValue(); + Long benId = ((Number) row[1]).longValue(); - regIdToBenIdMap.put(regId, benId); - resolvedBenIds.add(benId); - } + regIdToBenIdMap.put(regId, benId); + resolvedBenIds.add(benId); + } - // Fetch names for resolved BeneficiaryIds - if (!resolvedBenIds.isEmpty()) { - List resolvedDetails = - beneficiaryRepo.findBenNamesByBenIds( - new ArrayList<>(resolvedBenIds)); + // Fetch names for resolved BeneficiaryIds + if (!resolvedBenIds.isEmpty()) { + List resolvedDetails = + beneficiaryRepo.findBenNamesByBenIds( + new ArrayList<>(resolvedBenIds)); - for (Object[] row : resolvedDetails) { - Long benId = ((Number) row[0]).longValue(); + for (Object[] row : resolvedDetails) { + Long benId = ((Number) row[0]).longValue(); - String first = row[1] != null ? row[1].toString() : ""; - String last = row[2] != null ? row[2].toString() : ""; + String first = row[1] != null ? row[1].toString() : ""; + String last = row[2] != null ? row[2].toString() : ""; - benIdToNameMap.put(benId, (first + " " + last).trim()); - } + benIdToNameMap.put(benId, (first + " " + last).trim()); } } } - // NEW: Bulk fetch verifier (supervisor) roles — replaces N+1 userRepo.getUserRole() calls - // Bulk fetch verifier (supervisor) roles — replaces N+1 userRepo.getUserRole() calls - List verifierIds = entities.stream() - .filter(e -> e.getName() == null && e.getVerifiedByUserId() != null) - .map(e -> e.getVerifiedByUserId().intValue()) // adjust if getVerifiedByUserId() already returns Integer - .distinct() - .collect(Collectors.toList()); + } - Map verifierRoleMap = new HashMap<>(); - if (!verifierIds.isEmpty()) { - List roles = userRepo.getUserRolesByIds(verifierIds); - for (UserServiceRoleDTO r : roles) { - verifierRoleMap.put(r.getUserId(), r); - } - } + // Bulk fetch verifier (supervisor) roles — replaces N+1 userRepo.getUserRole() calls + List verifierIds = entities.stream() + .filter(e -> e.getName() == null && e.getVerifiedByUserId() != null) + .map(IncentiveActivityRecord::getVerifiedByUserId) + .distinct() + .collect(Collectors.toList()); - // Map entities to DTOs - List dtos = entities.stream().map(entry -> { - if (entry.getName() == null) { - Long originalId = entry.getBenId(); + Map verifierRoleMap = new HashMap<>(); + if (!verifierIds.isEmpty()) { + List roles = userRepo.getUserRolesByIds(verifierIds); + for (UserServiceRoleDTO r : roles) { + verifierRoleMap.put(r.getUserId(), r); + } + } - if (originalId != null && originalId > 0) { + // Map entities to DTOs + List finalDtos = entities.stream().map(entry -> { + if (entry.getName() == null) { + Long originalId = entry.getBenId(); - Long actualBenId = regIdToBenIdMap.getOrDefault( - originalId, originalId); + if (originalId != null && originalId > 0) { - String name = benIdToNameMap.getOrDefault(actualBenId, ""); + Long actualBenId = regIdToBenIdMap.getOrDefault( + originalId, originalId); - entry.setBenId(actualBenId); - entry.setName(name); + String name = benIdToNameMap.getOrDefault(actualBenId, ""); - if (isCG) { - entry.setIsEligible(true); - } + entry.setBenId(actualBenId); + entry.setName(name); - } else { - entry.setName(""); + if (isCG) { + entry.setIsEligible(true); } - if (entry.getVerifiedByUserId() != null) { - UserServiceRoleDTO role = verifierRoleMap.get(entry.getVerifiedByUserId()); - if (role != null) { - entry.setSupervisorRole(role.getRoleName()); - entry.setVerifiedByUserName(role.getName()); - } - } + } else { + entry.setName(""); + } - if (entry.getAshaId() != null) { - if (entry.getCreatedBy() == null) { - entry.setCreatedBy(userName); - } - if (entry.getUpdatedBy() == null) { - entry.setUpdatedBy(userName); - } - if (entry.getIsEligible() == null) { - entry.setIsEligible(true); - } + if (entry.getVerifiedByUserId() != null) { + UserServiceRoleDTO role = verifierRoleMap.get(entry.getVerifiedByUserId()); + if (role != null) { + entry.setSupervisorRole(role.getRoleName()); + entry.setVerifiedByUserName(role.getName()); } } - return modelMapper.map(entry, IncentiveRecordDTO.class); - }).collect(Collectors.toList()); - finalDtos.addAll(dtos); - page++; - - } while (pageResult.hasNext()); + if (entry.getAshaId() != null) { + if (entry.getCreatedBy() == null) { + entry.setCreatedBy(userName); + } + if (entry.getUpdatedBy() == null) { + entry.setUpdatedBy(userName); + } + if (entry.getIsEligible() == null) { + entry.setIsEligible(true); + } + } + } + return modelMapper.map(entry, IncentiveRecordDTO.class); + }).collect(Collectors.toList()); Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); return gson.toJson(finalDtos); @@ -420,7 +421,10 @@ public void resetRejectedIncentivesForPreviousMonths() { @Transactional(readOnly = true) public String getAllIncentivesGroupedSummary(GetBenRequestHandler request, Integer userId) { - String roleName = userService.getUserDetail(userId).getRoleName(); + List callerRoles = userRepo.getUserRole(userId); + String roleName = (callerRoles != null && !callerRoles.isEmpty()) + ? callerRoles.get(0).getRoleName() + : userService.getUserDetail(userId).getRoleName(); Integer reqStatus = request.getApprovalStatus(); Integer stateId = userRepo.getUserRole(request.getUserId()).get(0).getStateId(); @@ -428,24 +432,30 @@ public String getAllIncentivesGroupedSummary(GetBenRequestHandler request, Integ logger.info("Request getAllIncentivesGroupedSummary: {}", reqStatus); + LocalDate monthStart = LocalDate.of(request.getYear(), request.getMonth(), 1); + Timestamp startTs = Timestamp.valueOf(monthStart.atStartOfDay()); + Timestamp endTs = Timestamp.valueOf(monthStart.plusMonths(1).atStartOfDay()); + + // Filter by date range and isClaimed=true in DB instead of loading all historical records List records = - recordRepo.findRecordsByAsha(request.getUserId()) + recordRepo.findClaimedRecordsByAshaAndCreatedDateRange(request.getUserId(), startTs, endTs) .stream() - .filter(r -> r.getCreatedDate() != null) - .filter(r -> { - LocalDateTime created = r.getCreatedDate().toLocalDateTime(); - return created.getMonthValue() == request.getMonth() - && created.getYear() == request.getYear(); - }) - .filter(r -> Boolean.TRUE.equals(r.getIsClaimed())) .filter(r -> matchesRequestedStatus(r.getApprovalStatus(), reqStatus)) .collect(Collectors.toList()); + if (records.isEmpty()) { + return new Gson().toJson(Collections.emptyList()); + } + List activityIds = records.stream() .map(IncentiveActivityRecord::getActivityId) + .filter(Objects::nonNull) + .distinct() .collect(Collectors.toList()); - Set validActivityIds = incentivesRepo.findValidActivityIds(activityIds, isCG); + Set validActivityIds = activityIds.isEmpty() + ? Collections.emptySet() + : incentivesRepo.findValidActivityIds(activityIds, isCG); if (validActivityIds == null) { validActivityIds = Collections.emptySet(); } @@ -547,6 +557,12 @@ public String getAllIncentivesGroupedSummary(GetBenRequestHandler request, Integ Map> grouped = records.stream().collect(Collectors.groupingBy(IncentiveActivityRecord::getActivityId)); + // Bulk fetch all IncentiveActivity masters in ONE query instead of N findById() calls in loop + Map activityByIdMap = grouped.isEmpty() + ? Collections.emptyMap() + : incentivesRepo.findAllById(grouped.keySet()).stream() + .collect(Collectors.toMap(IncentiveActivity::getId, Function.identity())); + List> result = new ArrayList<>(); for (Map.Entry> entry : grouped.entrySet()) { @@ -554,7 +570,7 @@ public String getAllIncentivesGroupedSummary(GetBenRequestHandler request, Integ Long activityId = entry.getKey(); List list = entry.getValue(); - IncentiveActivity activity = incentivesRepo.findById(activityId).orElse(null); + IncentiveActivity activity = activityByIdMap.get(activityId); if (activity == null) continue; IncentiveActivityRecord latest = list.stream() @@ -648,19 +664,15 @@ public String getAllIncentivesGroupedActivity(GetBenRequestHandler request) { Timestamp startTs = Timestamp.valueOf(startDate.atStartOfDay()); Timestamp endTs = Timestamp.valueOf(endDate.atStartOfDay()); + // Filter by ashaId, activityId, and date range in DB instead of loading all records for the ASHA List records = - recordRepo.findRecordsByAsha(request.getUserId()) - .stream() - .filter(r -> - r.getActivityId() != null - && Objects.equals( - r.getActivityId(), - request.getActivityId() - ) - && r.getCreatedDate() != null - && !r.getCreatedDate().before(startTs) - && r.getCreatedDate().before(endTs) + recordRepo.findRecordsByAshaAndActivityAndCreatedDateRange( + request.getUserId(), + request.getActivityId(), + startTs, + endTs ) + .stream() .filter(r -> { Integer requestedStatus = @@ -743,7 +755,9 @@ public String getAllIncentivesGroupedActivity(GetBenRequestHandler request) { incentivesRepo.findById(request.getActivityId()).orElse(null); String groupName = activity != null ? activity.getGroup() : ""; - String groupCategoryName = activity != null ? activity.getGroupCategoryName() :activity.getGroup(); + String groupCategoryName = activity != null + ? (activity.getGroupCategoryName() != null ? activity.getGroupCategoryName() : activity.getGroup()) + : ""; String description = activity != null ? activity.getDescription() : ""; // 🔹 Map result @@ -888,11 +902,13 @@ public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boo if (updated > 0) { + Integer supervisorUserId = dashboardRepo.getSupervisorUserIdByAshaId(ashaId); + Map data = new HashMap<>(); data.put("notification_type", "INCENTIVE_APPROVAL"); data.put("nav_id", "INCENTIVE_HISTORY"); data.put("sender_user_id", String.valueOf(ashaId)); - data.put("receiver_user_id", String.valueOf(dashboardRepo.getSupervisorUserIdByAshaId(ashaId))); + data.put("receiver_user_id", String.valueOf(supervisorUserId)); data.put("month", String.valueOf(month)); data.put("year", String.valueOf(year)); data.put("approval_status", String.valueOf(102)); @@ -912,11 +928,16 @@ public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boo body, "INCENTIVE_CLAIMED", "INCENTIVE", - dashboardRepo.getSupervisorUserIdByAshaId(ashaId) + supervisorUserId ); + List ashaRoles = userRepo.getUserRole(ashaId); + String ashaName = (ashaRoles != null && !ashaRoles.isEmpty()) + ? ashaRoles.get(0).getName() + : userService.getUserDetail(ashaId).getName(); + String supervisorBody = - userService.getUserDetail(ashaId).getName() + ashaName + " successfully claimed incentive for month of " + Month.of(month).name() + " " @@ -929,7 +950,7 @@ public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boo supervisorBody, "INCENTIVE_CLAIMED", "INCENTIVE", - dashboardRepo.getSupervisorUserIdByAshaId(ashaId) + supervisorUserId ); } } @@ -942,69 +963,125 @@ public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boo } } - private void incentiveOfNcdReferal(Integer ashaId, Integer stateId, GetBenRequestHandler request) { + private void incentiveOfNcdReferal(Integer ashaId, Integer stateId, GetBenRequestHandler request, String userName) { try { - String userName = userRepo.getUserNamedByUserId(ashaId); + if (userName == null) { + userName = userRepo.getUserNamedByUserId(ashaId); + } + if (userName == null) { + return; + } String groupName = resolveGroupName(stateId); Map activityMap = incentivesRepo.findIncentiveMasterByNameAndGroup( List.of("NCD_POP_ENUMERATION", "HWC_REFERRAL_10_CASES"), groupName - ).stream().collect(Collectors.toMap(IncentiveActivity::getName, Function.identity())); + ).stream().collect(Collectors.toMap(IncentiveActivity::getName, Function.identity(), (a, b) -> a)); IncentiveActivity ncdPopEnumeration = activityMap.get("NCD_POP_ENUMERATION"); - IncentiveActivity hwcReferralEnumeration = activityMap.get("HWC_REFERRAL_10_CASES"); - CompletableFuture> benReferFuture = - CompletableFuture.supplyAsync(() -> benReferDetailsRepo.findByCreatedBy(userName)); - CompletableFuture> cbacFuture = - CompletableFuture.supplyAsync(() -> cbacIemrDetailsRepo.findByCreatedBy(userName)); - - List cbacDetailsImer = cbacFuture.get(); - - List recordsToSave = new ArrayList<>(); - + // 1. Optimize NCD_POP_ENUMERATION: fetch only (beneficiaryRegId, createdDate) instead of all 65 columns, + // check existing records in 1 bulk query instead of N individual queries, and batch insert. + if (ncdPopEnumeration != null) { + List cbacRows = cbacIemrDetailsRepo.findBenRegIdAndCreatedDateByCreatedBy(userName); + if (cbacRows != null && !cbacRows.isEmpty()) { + List existingRows = recordRepo.findBenIdAndStartDateByActivityIdAndAshaId( + ncdPopEnumeration.getId(), ashaId); + Set existingMonthKeys = new HashSet<>(); + if (existingRows != null) { + for (Object[] row : existingRows) { + Long existingBenId = row[0] != null ? ((Number) row[0]).longValue() : null; + Timestamp existingStart = (Timestamp) row[1]; + if (existingBenId != null && existingStart != null) { + LocalDateTime ldt = existingStart.toLocalDateTime(); + existingMonthKeys.add(existingBenId + "_" + ldt.getYear() + "_" + ldt.getMonthValue()); + } + } + } - if (ncdPopEnumeration != null && !cbacDetailsImer.isEmpty()) { + Timestamp nowTs = Timestamp.valueOf(LocalDateTime.now()); + Long rate = Long.valueOf(ncdPopEnumeration.getRate()); + List recordsToSave = new ArrayList<>(); + + for (Object[] cbacRow : cbacRows) { + if (cbacRow == null || cbacRow[0] == null || cbacRow[1] == null) continue; + Long benId = ((Number) cbacRow[0]).longValue(); + Timestamp createdDate = (Timestamp) cbacRow[1]; + LocalDateTime ldt = createdDate.toLocalDateTime(); + String monthKey = benId + "_" + ldt.getYear() + "_" + ldt.getMonthValue(); + + if (existingMonthKeys.add(monthKey)) { + IncentiveActivityRecord record = new IncentiveActivityRecord(); + record.setActivityId(ncdPopEnumeration.getId()); + record.setCreatedDate(createdDate); + record.setCreatedBy(userName); + record.setStartDate(createdDate); + record.setEndDate(createdDate); + record.setUpdatedDate(nowTs); + record.setUpdatedBy(userName); + record.setBenId(benId); + record.setAshaId(ashaId); + record.setAmount(rate); + recordsToSave.add(record); + } + } - final IncentiveActivity activity = ncdPopEnumeration; - cbacDetailsImer.forEach(cbacDetailsImer1 -> { - if(cbacDetailsImer1!=null){ - addNCDandCBACIncentiveRecord(activity, ashaId, cbacDetailsImer1.getBeneficiaryRegId(), cbacDetailsImer1.getCreatedDate(), userName); + if (!recordsToSave.isEmpty()) { + recordRepo.saveAll(recordsToSave); } - }); + } } + // 2. Optimize HWC_REFERRAL_10_CASES: check if monthly incentive already exists FIRST, + // and use index-friendly date range query instead of MONTH()/YEAR() functions. if (hwcReferralEnumeration != null) { - logger.info("hwcReferralEnumeration: {}", hwcReferralEnumeration.getDescription()); - logger.info("userName={}", userName); - - LocalDate requestDate = request.getFromDate() - .toInstant() - .atZone(ZoneId.systemDefault()) - .toLocalDate(); - - Long referralCount = benReferDetailsRepo.countMonthlyReferrals( - userName, - requestDate.getMonthValue(), - requestDate.getYear() - ); + LocalDate now = LocalDate.now(); + Timestamp startOfMonth = Timestamp.valueOf(now.withDayOfMonth(1).atStartOfDay()); + Timestamp endOfMonth = Timestamp.valueOf(now.withDayOfMonth(now.lengthOfMonth()).atTime(23, 59, 59)); - logger.info("referralCount: {}", referralCount); + IncentiveActivityRecord existingHwcRecord = recordRepo.findRecordByActivityIdCreatedDateBenId( + hwcReferralEnumeration.getId(), + 0L, + ashaId, + startOfMonth, + endOfMonth + ); - if (referralCount >= 10) { - addHwcReferalAshaIncentiveRecord( - hwcReferralEnumeration, - ashaId, - userName + if (existingHwcRecord == null) { + LocalDate requestDate = (request != null && request.getFromDate() != null) + ? request.getFromDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate() + : now; + LocalDate reqMonthFirst = requestDate.withDayOfMonth(1); + Timestamp reqStart = Timestamp.valueOf(reqMonthFirst.atStartOfDay()); + Timestamp reqEnd = Timestamp.valueOf(reqMonthFirst.plusMonths(1).atStartOfDay()); + + Long referralCount = benReferDetailsRepo.countMonthlyReferralsByDateRange( + userName, + reqStart, + reqEnd ); - } - } - // ---- Single batch insert instead of N individual saves ---- - if (!recordsToSave.isEmpty()) { - recordRepo.saveAll(recordsToSave); + logger.info("referralCount: {}", referralCount); + + if (referralCount != null && referralCount >= 10) { + Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now()); + IncentiveActivityRecord record = new IncentiveActivityRecord(); + record.setActivityId(hwcReferralEnumeration.getId()); + record.setCreatedDate(timestamp); + record.setCreatedBy(userName); + record.setStartDate(timestamp); + record.setEndDate(timestamp); + record.setUpdatedDate(timestamp); + record.setUpdatedBy(userName); + record.setBenId(0L); + record.setAshaId(ashaId); + record.setIsEligible(true); + record.setIsDefaultActivity(false); + record.setAmount(Long.valueOf(hwcReferralEnumeration.getRate())); + recordRepo.save(record); + } + } } } catch (Exception e) { @@ -1012,39 +1089,8 @@ private void incentiveOfNcdReferal(Integer ashaId, Integer stateId, GetBenReques } } - // Helper — record banana - private void addNCDandCBACIncentiveRecord(IncentiveActivity activity, Integer ashaId, - Long benId, Timestamp createdDate, String userName) { - - String lockKey = activity.getId() + "_" + benId + "_" + createdDate; - - Object lock = lockMap.computeIfAbsent(lockKey, k -> new Object()); - synchronized (lock){ - IncentiveActivityRecord incentiveActivityRecord = recordRepo.findRecordByActivityIdCreatedDateBenId(activity.getId(),createdDate,benId,ashaId); - if(incentiveActivityRecord==null){ - Timestamp now = Timestamp.valueOf(LocalDateTime.now()); - IncentiveActivityRecord record = new IncentiveActivityRecord(); - record.setActivityId(activity.getId()); - record.setCreatedDate(createdDate); - record.setCreatedBy(userName); - record.setStartDate(createdDate); - record.setEndDate(createdDate); - record.setUpdatedDate(now); - record.setUpdatedBy(userName); - record.setBenId(benId); - record.setAshaId(ashaId); - record.setAmount(Long.valueOf(activity.getRate())); - recordRepo.save(record); - } - lockMap.remove(lockKey, lock); - - } - - - } - private String resolveGroupName(Integer stateId) { - if (stateId.equals(StateCode.CG.getStateCode())) { + if (stateId != null && stateId.equals(StateCode.CG.getStateCode())) { logger.info("State id: {}", StateCode.CG.getStateCode()); return GroupName.ACTIVITY.getDisplayName(); } @@ -1052,198 +1098,159 @@ private String resolveGroupName(Integer stateId) { return GroupName.NCD.getDisplayName(); } - private void checkMonthlyAshaIncentive(Integer ashaId) { - + private void checkMonthlyAshaIncentive(Integer ashaId, String userName) { try { - String userName = userRepo.getUserNamedByUserId(ashaId); - - IncentiveActivity MOBILEBILLREIMB_ACTIVITY = incentivesRepo.findIncentiveMasterByNameAndGroup("MOBILE_BILL_REIMB", GroupName.OTHER_INCENTIVES.getDisplayName()); - IncentiveActivity ADDITIONAL_ASHA_INCENTIVE = incentivesRepo.findIncentiveMasterByNameAndGroup("ADDITIONAL_ASHA_INCENTIVE", GroupName.ADDITIONAL_INCENTIVE.getDisplayName()); - IncentiveActivity ASHA_MONTHLY_ROUTINE = incentivesRepo.findIncentiveMasterByNameAndGroup("ASHA_MONTHLY_ROUTINE", GroupName.ASHA_MONTHLY_ROUTINE.getDisplayName()); - if (MOBILEBILLREIMB_ACTIVITY != null) { - addMonthlyAshaIncentiveRecord(MOBILEBILLREIMB_ACTIVITY, ashaId, userName,false); - } - if (ADDITIONAL_ASHA_INCENTIVE != null) { - addMonthlyAshaIncentiveRecord(ADDITIONAL_ASHA_INCENTIVE, ashaId, userName,false); - + if (userName == null) { + userName = userRepo.getUserNamedByUserId(ashaId); } - - if (ASHA_MONTHLY_ROUTINE != null) { - addMonthlyAshaIncentiveRecord(ASHA_MONTHLY_ROUTINE, ashaId, userName,false); - + List candidates = incentivesRepo.findByNamesAndIsDeletedFalse( + List.of("MOBILE_BILL_REIMB", "ADDITIONAL_ASHA_INCENTIVE", "ASHA_MONTHLY_ROUTINE") + ); + List validActivities = new ArrayList<>(); + for (IncentiveActivity act : candidates) { + if (("MOBILE_BILL_REIMB".equals(act.getName()) && GroupName.OTHER_INCENTIVES.getDisplayName().equals(act.getGroup())) + || ("ADDITIONAL_ASHA_INCENTIVE".equals(act.getName()) && GroupName.ADDITIONAL_INCENTIVE.getDisplayName().equals(act.getGroup())) + || ("ASHA_MONTHLY_ROUTINE".equals(act.getName()) && GroupName.ASHA_MONTHLY_ROUTINE.getDisplayName().equals(act.getGroup()))) { + validActivities.add(act); + } } + addMonthlyAshaIncentiveRecordsBatch(validActivities, ashaId, userName, Collections.emptySet()); } catch (Exception e) { - logger.error("Error in addMonthlyAshaIncentiveRecord", e); - + logger.error("Error in checkMonthlyAshaIncentive", e); } - } - private void checkMonthlyAshaIncentiveForCg(Integer ashaId) { + private void checkMonthlyAshaIncentiveForCg(Integer ashaId, String userName) { try { - String userName = userRepo.getUserNamedByUserId(ashaId); - - IncentiveActivity MONTHLY_HONORARIUM = incentivesRepo.findIncentiveMasterByNameAndGroup("MONTHLY_HONORARIUM", GroupName.ACTIVITY.getDisplayName()); - IncentiveActivity MITANIN_REGISTER_5_INFO_FILL = incentivesRepo.findIncentiveMasterByNameAndGroup("MITANIN_REGISTER_5_INFO_FILL", GroupName.ACTIVITY.getDisplayName()); - IncentiveActivity MITANIN_REGISTER = incentivesRepo.findIncentiveMasterByNameAndGroup("MITANIN_REGISTER", GroupName.ACTIVITY.getDisplayName()); - if (MONTHLY_HONORARIUM != null) { - addMonthlyAshaIncentiveRecord(MONTHLY_HONORARIUM, ashaId, userName,true); - } - if (MITANIN_REGISTER_5_INFO_FILL != null) { - addMonthlyAshaIncentiveRecord(MITANIN_REGISTER_5_INFO_FILL, ashaId, userName,false); - - } - - if (MITANIN_REGISTER != null) { - addMonthlyAshaIncentiveRecord(MITANIN_REGISTER, ashaId, userName,false); - + if (userName == null) { + userName = userRepo.getUserNamedByUserId(ashaId); } + List validActivities = incentivesRepo.findIncentiveMasterByNameAndGroup( + List.of("MONTHLY_HONORARIUM", "MITANIN_REGISTER_5_INFO_FILL", "MITANIN_REGISTER"), + GroupName.ACTIVITY.getDisplayName() + ); + addMonthlyAshaIncentiveRecordsBatch(validActivities, ashaId, userName, Set.of("MONTHLY_HONORARIUM")); } catch (Exception e) { - logger.error("Error in addMonthlyAshaIncentiveRecord", e); - + logger.error("Error in checkMonthlyAshaIncentiveForCg", e); } - } - private void addMonthlyAshaIncentiveRecord(IncentiveActivity incentiveActivity, Integer ashaId, String userName,Boolean isApproved) { + private void addMonthlyAshaIncentiveRecordsBatch( + List activities, + Integer ashaId, + String userName, + Set preApprovedNames) { + if (activities == null || activities.isEmpty()) { + return; + } try { + LocalDate now = LocalDate.now(); Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now()); + Timestamp startOfMonth = Timestamp.valueOf(now.withDayOfMonth(1).atStartOfDay()); + Timestamp endOfMonth = Timestamp.valueOf(now.withDayOfMonth(now.lengthOfMonth()).atTime(23, 59, 59)); - Timestamp startOfMonth = Timestamp.valueOf(LocalDate.now().withDayOfMonth(1).atStartOfDay()); - Timestamp endOfMonth = Timestamp.valueOf(LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).atTime(23, 59, 59)); - - IncentiveActivityRecord record = recordRepo.findRecordByActivityIdCreatedDateBenId( - incentiveActivity.getId(), - 0L, - ashaId, - startOfMonth, - endOfMonth - ); - + List activityIds = activities.stream() + .map(IncentiveActivity::getId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); - if (record == null) { - record = new IncentiveActivityRecord(); - record.setActivityId(incentiveActivity.getId()); - record.setCreatedDate(timestamp); - record.setCreatedBy(userName); - record.setStartDate(timestamp); - record.setEndDate(timestamp); - record.setUpdatedDate(timestamp); - record.setUpdatedBy(userName); - record.setBenId(0L); - record.setAshaId(ashaId); - record.setIsEligible(true); - record.setIsDefaultActivity(true); - record.setIsApproved(isApproved); - record.setAmount(Long.valueOf(incentiveActivity.getRate())); - recordRepo.save(record); + if (activityIds.isEmpty()) { + return; } - } catch (Exception e) { - logger.error("Error in addMonthlyAshaIncentiveRecord", e); - - } - } - - private void addHwcReferalAshaIncentiveRecord(IncentiveActivity incentiveActivity, Integer ashaId, String userName) { - try { - Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now()); - - Timestamp startOfMonth = Timestamp.valueOf(LocalDate.now().withDayOfMonth(1).atStartOfDay()); - Timestamp endOfMonth = Timestamp.valueOf(LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).atTime(23, 59, 59)); - - IncentiveActivityRecord record = recordRepo.findRecordByActivityIdCreatedDateBenId( - incentiveActivity.getId(), - 0L, - ashaId, - startOfMonth, - endOfMonth + Set existingIds = new HashSet<>( + recordRepo.findExistingActivityIdsByMonth(activityIds, 0L, ashaId, startOfMonth, endOfMonth) ); + List newRecords = new ArrayList<>(); + for (IncentiveActivity activity : activities) { + if (activity != null && !existingIds.contains(activity.getId())) { + IncentiveActivityRecord record = new IncentiveActivityRecord(); + record.setActivityId(activity.getId()); + record.setCreatedDate(timestamp); + record.setCreatedBy(userName); + record.setStartDate(timestamp); + record.setEndDate(timestamp); + record.setUpdatedDate(timestamp); + record.setUpdatedBy(userName); + record.setBenId(0L); + record.setAshaId(ashaId); + record.setIsEligible(true); + record.setIsDefaultActivity(true); + record.setIsApproved(preApprovedNames != null && preApprovedNames.contains(activity.getName())); + record.setAmount(Long.valueOf(activity.getRate())); + newRecords.add(record); + } + } - if (record == null) { - record = new IncentiveActivityRecord(); - record.setActivityId(incentiveActivity.getId()); - record.setCreatedDate(timestamp); - record.setCreatedBy(userName); - record.setStartDate(timestamp); - record.setEndDate(timestamp); - record.setUpdatedDate(timestamp); - record.setUpdatedBy(userName); - record.setBenId(0L); - record.setAshaId(ashaId); - record.setIsEligible(true); - record.setIsDefaultActivity(false); - record.setAmount(Long.valueOf(incentiveActivity.getRate())); - recordRepo.save(record); + if (!newRecords.isEmpty()) { + recordRepo.saveAll(newRecords); } } catch (Exception e) { - logger.error("Error in addMonthlyAshaIncentiveRecord", e); - + logger.error("Error in addMonthlyAshaIncentiveRecordsBatch", e); } - } - private void addIncentiveForIronTablets(Integer userId) { - IncentiveActivity incentiveActivityAM = incentivesRepo.findIncentiveMasterByNameAndGroup("NATIONAL_IRON_PLUS", GroupName.CHILD_HEALTH.getDisplayName()); - IncentiveActivity incentiveActivityCG = incentivesRepo.findIncentiveMasterByNameAndGroup("NATIONAL_IRON_PLUS", GroupName.ACTIVITY.getDisplayName()); - - String userName = userService.getUserDetail(userId).getUserName(); - Integer stateId = userService.getUserDetail(userId).getStateId(); - if (userName != null) { - List eligibleCoupleRegisters = eligibleCoupleRegisterRepo.findByCreatedBy(userName); - List ifaFormSubmissionData = ifaFormSubmissionRepository.findByUserId(userId); - logger.info("eligibleCoupleRegisters :" + eligibleCoupleRegisters.size()); - logger.info("ifaFormSubmissionData :" + ifaFormSubmissionData.size()); - - if (!eligibleCoupleRegisters.isEmpty() && !ifaFormSubmissionData.isEmpty()) { - - int percentage = (int) (((double) ifaFormSubmissionData.size() - / eligibleCoupleRegisters.size()) * 100); - - logger.info("IFA Count : {}", ifaFormSubmissionData.size()); - logger.info("Eligible Couple Count : {}", eligibleCoupleRegisters.size()); - logger.info("Percentage : {}", percentage); + private void addIncentiveForIronTablets(Integer userId, String userName, Integer stateId) { + if (userName == null || stateId == null) { + return; + } - if (percentage >= 50) { + IncentiveActivity targetActivity = null; + if (stateId.equals(StateCode.AM.getStateCode())) { + targetActivity = incentivesRepo.findIncentiveMasterByNameAndGroup("NATIONAL_IRON_PLUS", GroupName.CHILD_HEALTH.getDisplayName()); + } else if (stateId.equals(StateCode.CG.getStateCode())) { + targetActivity = incentivesRepo.findIncentiveMasterByNameAndGroup("NATIONAL_IRON_PLUS", GroupName.ACTIVITY.getDisplayName()); + } - if (stateId.equals(StateCode.AM.getStateCode())) { - addIFAIncentive( - ifaFormSubmissionData.get(ifaFormSubmissionData.size() - 1), - incentiveActivityAM, - userId, - userName); - } + if (targetActivity == null) { + return; + } - if (stateId.equals(StateCode.CG.getStateCode())) { - addIFAIncentive( - ifaFormSubmissionData.get(ifaFormSubmissionData.size() - 1), - incentiveActivityCG, - userId, - userName); - } - } - } + LocalDate now = LocalDate.now(); + Timestamp startOfMonth = Timestamp.valueOf(now.withDayOfMonth(1).atStartOfDay()); + Timestamp endOfMonth = Timestamp.valueOf(now.withDayOfMonth(now.lengthOfMonth()).atTime(23, 59, 59)); + + // Check if IFA incentive record already exists for this month BEFORE querying EC & IFA tables + IncentiveActivityRecord existingRecord = recordRepo.findRecordByActivityIdCreatedDateBenId( + targetActivity.getId(), + 0L, + userId, + startOfMonth, + endOfMonth + ); + if (existingRecord != null) { + return; + } + // Use COUNT(*) queries instead of loading full entities (and LONGTEXT photos) into memory + long ecCount = eligibleCoupleRegisterRepo.countByCreatedBy(userName); + if (ecCount <= 0) { + return; + } + long ifaCount = ifaFormSubmissionRepository.countByUserId(userId); + if (ifaCount <= 0) { + return; } + int percentage = (int) (((double) ifaCount / ecCount) * 100); + logger.info("IFA Count : {}, Eligible Couple Count : {}, Percentage : {}", ifaCount, ecCount, percentage); + if (percentage >= 50) { + addIFAIncentive(targetActivity, userId, userName, startOfMonth, endOfMonth); + } } - private void addIFAIncentive(IFAFormSubmissionData ifaFormSubmissionData, IncentiveActivity incentiveActivityAM, Integer userId, String userName) { - Timestamp startOfMonth = Timestamp.valueOf(LocalDate.now().withDayOfMonth(1).atStartOfDay()); - Timestamp endOfMonth = Timestamp.valueOf(LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).atTime(23, 59, 59)); - - String lockKey = incentiveActivityAM.getId() + "_" + 0 + "_" + startOfMonth; - + private void addIFAIncentive(IncentiveActivity incentiveActivity, Integer userId, String userName, + Timestamp startOfMonth, Timestamp endOfMonth) { + String lockKey = incentiveActivity.getId() + "_" + 0 + "_" + startOfMonth; Object lock = lockMap.computeIfAbsent(lockKey, k -> new Object()); Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now()); - logger.info("IFA incentive"); - synchronized (lock){ + synchronized (lock) { IncentiveActivityRecord incentiveActivityRecord = recordRepo.findRecordByActivityIdCreatedDateBenId( - incentiveActivityAM.getId(), + incentiveActivity.getId(), 0L, userId, startOfMonth, @@ -1251,26 +1258,21 @@ private void addIFAIncentive(IFAFormSubmissionData ifaFormSubmissionData, Incent ); if (incentiveActivityRecord == null) { incentiveActivityRecord = new IncentiveActivityRecord(); - incentiveActivityRecord.setActivityId(incentiveActivityAM.getId()); + incentiveActivityRecord.setActivityId(incentiveActivity.getId()); incentiveActivityRecord.setCreatedDate(timestamp); incentiveActivityRecord.setStartDate(timestamp); incentiveActivityRecord.setEndDate(timestamp); incentiveActivityRecord.setUpdatedDate(timestamp); - incentiveActivityRecord.setUpdatedBy(ifaFormSubmissionData.getUserName()); - incentiveActivityRecord.setCreatedBy(ifaFormSubmissionData.getUserName()); + incentiveActivityRecord.setUpdatedBy(userName); + incentiveActivityRecord.setCreatedBy(userName); incentiveActivityRecord.setBenId(0L); incentiveActivityRecord.setAshaId(userId); - incentiveActivityRecord.setAmount(Long.valueOf(incentiveActivityAM.getRate())); + incentiveActivityRecord.setAmount(Long.valueOf(incentiveActivity.getRate())); recordRepo.save(incentiveActivityRecord); logger.info("saved IFA incentive"); - } - } lockMap.remove(lockKey, lock); - - } - } From f52c4c6162f6e92e15160709861887789577a371 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 28 Oct 2026 17:28:07 +0530 Subject: [PATCH 20/24] Add api for general checkup follow up --- .../controller/DiseaseControlController.java | 2 +- .../GeneralCheckUpFollowUpRepository.java | 3 + .../impl/DiseaseControlServiceImpl.java | 78 ++++++++++--------- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java index 318b29bad..b821fd9ab 100644 --- a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java +++ b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java @@ -445,7 +445,7 @@ public ResponseEntity> savGeneralCheckUp( } - @RequestMapping(value = "generalCheckUpFollowUp/getAll", method = RequestMethod.POST) + @RequestMapping(value = "generalCheckUpFollowUp/getAll", method = RequestMethod.POST) public ResponseEntity> getGeneralCheckUpFollowUp(@RequestBody GetBenRequestHandler getBenRequestHandler,@RequestHeader(value = "JwtToken") String token) { Map response = new LinkedHashMap<>(); diff --git a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java index 1b6f0d4b7..14980f863 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java +++ b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java @@ -4,7 +4,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface GeneralCheckUpFollowUpRepository extends JpaRepository { + List findByAshaId(Integer userId); } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index a49527d3a..aa7196782 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1758,45 +1758,51 @@ public List saveGeneralCheckUpFollowUp( } @Override - @Transactional(readOnly = true) + @Transactional() public List getGeneralCheckUpFollowUp( String token) { + try { + Integer userId = jwtUtil.extractUserId(token); + + List entities = + generalCheckUpFollowUpRepository.findByAshaId(userId); + + return entities.stream() + .map(entity -> { + GeneralCheckUpFollowUpDto dto = + new GeneralCheckUpFollowUpDto(); + + dto.setId(entity.getId()); + dto.setBeneficiaryId(entity.getBenId()); + dto.setHouseHoldId(entity.getHouseholdId()); + + GeneralCheckUpFollowUpListDto fields = + new GeneralCheckUpFollowUpListDto(); + + fields.setFollowUpDate( + entity.getFollowUpDate() != null + ? entity.getFollowUpDate().toString() + : null); + + fields.setFollowupStatus(entity.getFollowupStatus()); + fields.setReferredTo(entity.getReferredTo()); + + dto.setFields(fields); + + dto.setVisitDate( + entity.getFollowUpDate() != null + ? entity.getFollowUpDate().toString() + : null); + + return dto; + }) + .collect(Collectors.toList()); + } catch (Exception e) { + // Replace with your project's standard IEMRException constructor. + throw new RuntimeException( + "Error saving General Check-up follow-up", e); + } - Integer userId = getUserIdFromToken(token); - - List entities = - generalCheckUpFollowUpRepository.findByAshaId(userId); - - return entities.stream() - .map(entity -> { - GeneralCheckUpFollowUpDto dto = - new GeneralCheckUpFollowUpDto(); - - dto.setId(entity.getId()); - dto.setBeneficiaryId(entity.getBenId()); - dto.setHouseHoldId(entity.getHouseholdId()); - - GeneralCheckUpFollowUpListDto fields = - new GeneralCheckUpFollowUpListDto(); - - fields.setFollowUpDate( - entity.getFollowUpDate() != null - ? entity.getFollowUpDate().toString() - : null); - - fields.setFollowupStatus(entity.getFollowupStatus()); - fields.setReferredTo(entity.getReferredTo()); - - dto.setFields(fields); - - dto.setVisitDate( - entity.getFollowUpDate() != null - ? entity.getFollowUpDate().toString() - : null); - - return dto; - }) - .collect(Collectors.toList()); } @Override From 585b82412e830330fd8e274c5d5acf1f2c762029 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 28 Oct 2026 18:50:38 +0530 Subject: [PATCH 21/24] Add api for general checkup follow up --- .../controller/DiseaseControlController.java | 65 ---------- .../GeneralCheckUpFollowUpController.java | 121 ++++++++++++++++++ 2 files changed, 121 insertions(+), 65 deletions(-) create mode 100644 src/main/java/com/iemr/flw/controller/GeneralCheckUpFollowUpController.java diff --git a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java index b821fd9ab..57b4e3b49 100644 --- a/src/main/java/com/iemr/flw/controller/DiseaseControlController.java +++ b/src/main/java/com/iemr/flw/controller/DiseaseControlController.java @@ -445,70 +445,5 @@ public ResponseEntity> savGeneralCheckUp( } - @RequestMapping(value = "generalCheckUpFollowUp/getAll", method = RequestMethod.POST) - public ResponseEntity> getGeneralCheckUpFollowUp(@RequestBody GetBenRequestHandler getBenRequestHandler,@RequestHeader(value = "JwtToken") String token) { - - Map response = new LinkedHashMap<>(); - - try { - List result = - diseaseControlService.getGeneralCheckUpFollowUp(token); - - if (result != null && !result.isEmpty()) { - response.put("statusCode", HttpStatus.OK.value()); - response.put("data", result); - return ResponseEntity.ok(response); - } else { - response.put("statusCode", HttpStatus.NOT_FOUND.value()); - response.put("message", "No records found"); - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); - } - - } catch (Exception e) { - logger.error("Error fetching Chronic Disease Visit :", e); - response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); - response.put("errorMessage", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } - - - @RequestMapping(value = "generalCheckUpFollowUp/saveAll", method = RequestMethod.POST) - public ResponseEntity> savGeneralCheckUpFollowUp( - @RequestBody List requestList,@RequestHeader(value = "JwtToken") String token) { - - Map response = new LinkedHashMap<>(); - logger.info("####################################################"); - try { - logger.info("General Checkup Save Request: {}", - objectMapper.writerWithDefaultPrettyPrinter() - .writeValueAsString(requestList)); - } catch (Exception e) { - logger.error("Failed to convert request to JSON", e); - } - logger.info("####################################################"); - - try { - List savedList = - diseaseControlService.saveGeneralCheckUpFollowUp(requestList,token); - - if (savedList != null && !savedList.isEmpty()) { - response.put("statusCode", HttpStatus.OK.value()); - response.put("message", "Data saved successfully"); - response.put("data", savedList); - return ResponseEntity.ok(response); - } else { - response.put("statusCode", HttpStatus.BAD_REQUEST.value()); - response.put("message", "No data saved"); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); - } - - } catch (Exception e) { - logger.error("Error saving Chronic Disease Visit :", e); - response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); - response.put("errorMessage", e.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); - } - } } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/controller/GeneralCheckUpFollowUpController.java b/src/main/java/com/iemr/flw/controller/GeneralCheckUpFollowUpController.java new file mode 100644 index 000000000..ca450d670 --- /dev/null +++ b/src/main/java/com/iemr/flw/controller/GeneralCheckUpFollowUpController.java @@ -0,0 +1,121 @@ +/* +* 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 +*/ +package com.iemr.flw.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.*; +import com.iemr.flw.service.DiseaseControlService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping(value = "/generalCheckUpFollowUp",headers = "Authorization") +public class GeneralCheckUpFollowUpController { + private final Logger logger = LoggerFactory.getLogger(GeneralCheckUpFollowUpController.class); + + @Autowired + private DiseaseControlService diseaseControlService; + + @Autowired + private ObjectMapper objectMapper; + + + + @RequestMapping(value = "getAll", method = RequestMethod.POST) + public ResponseEntity> getGeneralCheckUpFollowUp(@RequestBody GetBenRequestHandler getBenRequestHandler,@RequestHeader(value = "JwtToken") String token) { + + Map response = new LinkedHashMap<>(); + + try { + List result = + diseaseControlService.getGeneralCheckUpFollowUp(token); + + if (result != null && !result.isEmpty()) { + response.put("statusCode", HttpStatus.OK.value()); + response.put("data", result); + return ResponseEntity.ok(response); + } else { + response.put("statusCode", HttpStatus.NOT_FOUND.value()); + response.put("message", "No records found"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + + } catch (Exception e) { + logger.error("Error fetching Chronic Disease Visit :", e); + response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("errorMessage", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + + @RequestMapping(value = "saveAll", method = RequestMethod.POST) + public ResponseEntity> savGeneralCheckUpFollowUp( + @RequestBody List requestList,@RequestHeader(value = "JwtToken") String token) { + + Map response = new LinkedHashMap<>(); + logger.info("####################################################"); + try { + logger.info("General Checkup Save Request: {}", + objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(requestList)); + } catch (Exception e) { + logger.error("Failed to convert request to JSON", e); + } + logger.info("####################################################"); + + try { + List savedList = + diseaseControlService.saveGeneralCheckUpFollowUp(requestList,token); + + if (savedList != null && !savedList.isEmpty()) { + response.put("statusCode", HttpStatus.OK.value()); + response.put("message", "Data saved successfully"); + response.put("data", savedList); + return ResponseEntity.ok(response); + } else { + response.put("statusCode", HttpStatus.BAD_REQUEST.value()); + response.put("message", "No data saved"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + } catch (Exception e) { + logger.error("Error saving Chronic Disease Visit :", e); + response.put("statusCode", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("errorMessage", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + +} \ No newline at end of file From 0063bd12fa662d620e5949313fe66f4f1a7cf03e Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Thu, 29 Oct 2026 12:38:51 +0530 Subject: [PATCH 22/24] Make multiple followUp in HWC referal --- .../domain/iemr/GeneralCheckUpFollowUp.java | 6 + .../dto/iemr/GeneralCheckUpFollowUpDto.java | 4 + .../GeneralCheckUpFollowUpRepository.java | 11 +- .../impl/DiseaseControlServiceImpl.java | 103 +++++++++++++----- 4 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java index f907e377a..3457fdf4f 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java @@ -45,4 +45,10 @@ public class GeneralCheckUpFollowUp { @Column(name = "updated_date", insertable = false, updatable = false) private Timestamp updatedDate; + + @Column(name = "referralReason") + private String referralReason; + + @Column(name = "referredOnDate") + private LocalDate referredOnDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java index eebd3b24e..9840e0d0e 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java +++ b/src/main/java/com/iemr/flw/dto/iemr/GeneralCheckUpFollowUpDto.java @@ -2,11 +2,15 @@ import lombok.Data; +import java.time.LocalDate; + @Data public class GeneralCheckUpFollowUpDto { private Long id; private Long beneficiaryId; private Long houseHoldId; private String visitDate; + private String referralReason; + private LocalDate referredOnDate; private GeneralCheckUpFollowUpListDto fields; } diff --git a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java index 14980f863..cef915267 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java +++ b/src/main/java/com/iemr/flw/repo/iemr/GeneralCheckUpFollowUpRepository.java @@ -4,10 +4,19 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.time.LocalDate; import java.util.List; +import java.util.Optional; @Repository public interface GeneralCheckUpFollowUpRepository extends JpaRepository { List findByAshaId(Integer userId); -} \ No newline at end of file + + Optional + findFirstByBenIdAndReferralReasonAndReferredOnDateGreaterThanEqualAndReferredOnDateLessThan( + Long benId, + String referralReason, + LocalDate monthStart, + LocalDate nextMonthStart);} + diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index aa7196782..fe95b878d 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1648,6 +1648,7 @@ public List saveGeneralCheckUp(List genera return generalCheckUpRequest; } + @Override @Transactional public List saveGeneralCheckUpFollowUp( @@ -1662,7 +1663,7 @@ public List saveGeneralCheckUpFollowUp( Integer userId = jwtUtil.extractUserId(token); String username = jwtUtil.extractUsername(token); - List entities = new ArrayList<>(); + List result = new ArrayList<>(); for (GeneralCheckUpFollowUpDto dto : dtos) { @@ -1671,6 +1672,30 @@ public List saveGeneralCheckUpFollowUp( "Follow-up details are required"); } + if (dto.getBeneficiaryId() == null) { + throw new IllegalArgumentException( + "Beneficiary ID is required"); + } + + // Referral-specific fields + String referralReason = dto.getReferralReason(); + LocalDate referredOnDate = dto.getReferredOnDate(); + + if (referralReason == null || referralReason.isBlank()) { + throw new IllegalArgumentException( + "Referral reason is required"); + } + + if (referredOnDate == null) { + throw new IllegalArgumentException( + "Referral date is required"); + } + + + + LocalDate monthStart = referredOnDate.withDayOfMonth(1); + LocalDate nextMonthStart = monthStart.plusMonths(1); + GeneralCheckUpFollowUpListDto fields = dto.getFields(); String date = fields.getFollowUpDate(); @@ -1684,8 +1709,8 @@ public List saveGeneralCheckUpFollowUp( "Follow-up date is required"); } - LocalDate followUpDate = LocalDate.parse( - date.substring(0, 10)); + LocalDate followUpDate = + LocalDate.parse(date.substring(0, 10)); String status = fields.getFollowupStatus(); @@ -1695,7 +1720,8 @@ public List saveGeneralCheckUpFollowUp( } boolean referredToHigherFacility = - "referred_to_higher_facility".equalsIgnoreCase(status); + "referred_to_higher_facility" + .equalsIgnoreCase(status); if (referredToHigherFacility && (fields.getReferredTo() == null @@ -1704,11 +1730,23 @@ public List saveGeneralCheckUpFollowUp( "Referred-to facility is required"); } + // Find the existing record for this specific referral. GeneralCheckUpFollowUp entity = - new GeneralCheckUpFollowUp(); - + generalCheckUpFollowUpRepository + .findFirstByBenIdAndReferralReasonAndReferredOnDateGreaterThanEqualAndReferredOnDateLessThan( + dto.getBeneficiaryId(), + referralReason, + monthStart, + nextMonthStart) + .orElseGet(GeneralCheckUpFollowUp::new); + + // Insert if no matching record exists; otherwise update it. entity.setBenId(dto.getBeneficiaryId()); entity.setHouseholdId(dto.getHouseHoldId()); + + entity.setReferralReason(referralReason); + entity.setReferredOnDate(referredOnDate); + entity.setFollowUpDate(followUpDate); entity.setFollowupStatus(status); @@ -1721,37 +1759,41 @@ public List saveGeneralCheckUpFollowUp( entity.setAshaId(userId); entity.setCreatedBy(username); - entities.add(entity); - } + GeneralCheckUpFollowUp savedEntity = + generalCheckUpFollowUpRepository.save(entity); + + // Return the saved record with referral information. + GeneralCheckUpFollowUpDto response = + new GeneralCheckUpFollowUpDto(); - List savedEntities = - generalCheckUpFollowUpRepository.saveAll(entities); + response.setId(savedEntity.getId()); + response.setBeneficiaryId(savedEntity.getBenId()); + response.setHouseHoldId(savedEntity.getHouseholdId()); - return savedEntities.stream() - .map(entity -> { - GeneralCheckUpFollowUpDto dto = - new GeneralCheckUpFollowUpDto(); + response.setReferralReason(savedEntity.getReferralReason()); + response.setReferredOnDate( + savedEntity.getReferredOnDate() != null + ? savedEntity.getReferredOnDate() + : null + ); + + GeneralCheckUpFollowUpListDto responseFields = + new GeneralCheckUpFollowUpListDto(); - dto.setId(entity.getId()); - dto.setBeneficiaryId(entity.getBenId()); - dto.setHouseHoldId(entity.getHouseholdId()); + responseFields.setFollowUpDate( + savedEntity.getFollowUpDate().toString()); + responseFields.setFollowupStatus( + savedEntity.getFollowupStatus()); + responseFields.setReferredTo(savedEntity.getReferredTo()); - GeneralCheckUpFollowUpListDto fields = - new GeneralCheckUpFollowUpListDto(); + response.setFields(responseFields); - fields.setFollowUpDate( - entity.getFollowUpDate().toString()); - fields.setFollowupStatus( - entity.getFollowupStatus()); - fields.setReferredTo(entity.getReferredTo()); + result.add(response); + } - dto.setFields(fields); - return dto; - }) - .collect(Collectors.toList()); + return result; } catch (Exception e) { - // Replace with your project's standard IEMRException constructor. throw new RuntimeException( "Error saving General Check-up follow-up", e); } @@ -1775,6 +1817,8 @@ public List getGeneralCheckUpFollowUp( dto.setId(entity.getId()); dto.setBeneficiaryId(entity.getBenId()); dto.setHouseHoldId(entity.getHouseholdId()); + dto.setReferralReason(entity.getReferralReason()); + dto.setReferredOnDate(entity.getReferredOnDate()); GeneralCheckUpFollowUpListDto fields = new GeneralCheckUpFollowUpListDto(); @@ -1787,6 +1831,7 @@ public List getGeneralCheckUpFollowUp( fields.setFollowupStatus(entity.getFollowupStatus()); fields.setReferredTo(entity.getReferredTo()); + dto.setFields(fields); dto.setVisitDate( From 21de33b427cb2bd1fa72e311f1427fce7f5634a4 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Thu, 29 Oct 2026 15:22:33 +0530 Subject: [PATCH 23/24] Make multiple followUp in HWC referal --- src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java | 1 - .../java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java | 4 ++-- .../com/iemr/flw/service/impl/DiseaseControlServiceImpl.java | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java index f3d84c52a..ec9f9c64b 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUp.java @@ -52,7 +52,6 @@ public class GeneralCheckUp { private String priority; - @Column(name = "referral_reason", length = 500) private String referralReason; diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java index 3457fdf4f..6961a9ace 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java @@ -46,9 +46,9 @@ public class GeneralCheckUpFollowUp { @Column(name = "updated_date", insertable = false, updatable = false) private Timestamp updatedDate; - @Column(name = "referralReason") + @Column(name = "referral_reason") private String referralReason; - @Column(name = "referredOnDate") + @Column(name = "referred_on_date") private LocalDate referredOnDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java index fe95b878d..aa00718be 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -1758,6 +1758,7 @@ public List saveGeneralCheckUpFollowUp( entity.setAshaId(userId); entity.setCreatedBy(username); + entity.setReferredTo(dto.getFields().getReferredTo()); GeneralCheckUpFollowUp savedEntity = generalCheckUpFollowUpRepository.save(entity); From d2aa09baa6009efc0a1da3928761e37366dcaa87 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Thu, 29 Oct 2026 15:24:49 +0530 Subject: [PATCH 24/24] Make multiple followUp in HWC referal --- .../java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java index 6961a9ace..3457fdf4f 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralCheckUpFollowUp.java @@ -46,9 +46,9 @@ public class GeneralCheckUpFollowUp { @Column(name = "updated_date", insertable = false, updatable = false) private Timestamp updatedDate; - @Column(name = "referral_reason") + @Column(name = "referralReason") private String referralReason; - @Column(name = "referred_on_date") + @Column(name = "referredOnDate") private LocalDate referredOnDate; } \ No newline at end of file