diff --git a/.github/workflows/swagger-json.yml b/.github/workflows/swagger-json.yml new file mode 100644 index 000000000..2c2fb910a --- /dev/null +++ b/.github/workflows/swagger-json.yml @@ -0,0 +1,106 @@ +name: Sync Swagger to AMRIT-Docs + +on: + push: + branches: [ main ] + workflow_dispatch: + +jobs: + swagger-sync: + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Checkout API repo + uses: actions/checkout@v4 + + - name: Set up Java 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + cache: maven + + - name: Build API (skip tests) + run: mvn -B clean package -DskipTests -DENV_VAR=local -Pswagger + + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Run API in swagger profile + run: | + nohup java -jar target/flw-api-*.war \ + --spring.profiles.active=swagger \ + --server.port=9090 \ + > app.log 2>&1 & + echo $! > api_pid.txt + + - name: Wait for API & fetch Swagger + run: | + for i in {1..40}; do + CODE=$(curl --connect-timeout 2 --max-time 5 -s -o swagger_raw.json -w "%{http_code}" http://localhost:9090/v3/api-docs || true) + + if [ "$CODE" = "200" ]; then + jq . swagger_raw.json > flw-api.json || { + echo "Swagger JSON invalid" + cat swagger_raw.json + exit 1 + } + + if [ "$(jq '.paths | length' flw-api.json)" -eq 0 ]; then + echo "Swagger paths empty – failing" + exit 1 + fi + + echo "Swagger generated successfully" + exit 0 + fi + + echo "Waiting for API... ($i)" + sleep 4 + done + + echo "Swagger not generated" + cat app.log || true + exit 1 + + - name: Stop API + if: always() + run: | + # Kill the process directly by PID (process-group kill is unreliable in CI) + sleep 5 + if [ -f api_pid.txt ]; then + PID=$(cat api_pid.txt) + kill -TERM "$PID" 2>/dev/null || true + sleep 2 + kill -9 "$PID" 2>/dev/null || true + fi + # Fallback: kill any remaining java process on port 9090 + fuser -k 9090/tcp 2>/dev/null || true + + - name: Checkout AMRIT-Docs + uses: actions/checkout@v4 + with: + repository: PSMRI/AMRIT-Docs + token: ${{ secrets.DOCS_REPO_TOKEN }} + path: amrit-docs + fetch-depth: 0 + + - name: Copy Swagger JSON + run: | + mkdir -p amrit-docs/docs/swagger + cp flw-api.json amrit-docs/docs/swagger/flw-api.json + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v8 + with: + token: ${{ secrets.DOCS_REPO_TOKEN }} + path: amrit-docs + branch: auto/swagger-update-flw-api + base: main + commit-message: "chore(docs): auto-update FLW-API swagger" + title: "chore(docs): auto-update FLW-API swagger" + delete-branch: true + body: | + This PR automatically updates FLW-API Swagger JSON + from the latest main branch build. diff --git a/.github/workflows/testcase-coverage.yml b/.github/workflows/testcase-coverage.yml new file mode 100644 index 000000000..5e71c85c6 --- /dev/null +++ b/.github/workflows/testcase-coverage.yml @@ -0,0 +1,44 @@ +name: TestCase Coverage Check + +on: + pull_request: + types: [opened, synchronize] + +permissions: {} + +jobs: + build_and_check_coverage: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Maven dependencies + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-m2- + + - name: Build with Maven + run: mvn clean verify + + - name: Run Coverage Check + uses: madrapps/jacoco-report@v1.7.2 + with: + paths: target/site/jacoco/jacoco.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 40 + min-coverage-changed-files: 60 + title: Code Coverage Report + comment-type: pr_comment \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5a1e6d24a..a4786a90b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,3 @@ mvnw.cmd .mvn /logs/ src/main/environment/common_local.properties -node_modules diff --git a/README.md b/README.md index 5c992214d..7f8d7e052 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # Frontline Health Workers (FLW) Mobile App API Module +[![DeepWiki](https://img.shields.io/badge/DeepWiki-PSMRI%2FFLW--API-blue)](https://deepwiki.com/PSMRI/FLW-API) + + ### Introduction Frontline Health Workers (FLW) Mobile App is known with brand name _SAKHI_ App. Currently this _SAKHI_ App is designed for Accredited Social Health Activist (ASHA). diff --git a/pom.xml b/pom.xml index 5fc0bc731..63e8f2ff9 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ ${ENV_VAR} target/classes/application.properties target/classes/common_${environment}.properties - true + false target @@ -218,11 +218,6 @@ org.slf4j slf4j-api - - org.jacoco - jacoco-maven-plugin - 0.8.11 - org.springframework spring-web @@ -262,6 +257,17 @@ + + swagger + + + + com.h2database + h2 + runtime + + + 1097_flw @@ -281,15 +287,30 @@ ${artifactId}-${version} - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - org.springframework.boot - spring-boot-maven-plugin - + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + + prepare-agent + + + + report + test + + report + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index ad996282d..b778ecfff 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -1,6 +1,7 @@ fhir-url = @env.FHIR_API@ tm-url = @env.TM_API@ +common-api-base-url=@env.COMMON_API@ ##--------------------------------------------## Primary db------------------------------------------------------------------- @@ -27,11 +28,6 @@ jwt.secret=@env.JWT_SECRET_KEY@ # Redis Config spring.redis.host=@env.REDIS_HOST@ -# Stop TB: when true, saves fail with an error if camp (vanID) is not configured -stoptb.enforce.vanid=@env.STOPTB_ENFORCE_VANID@ -# Stop TB: this deployment's van/camp ID, replacing the old Redis camp:vanID lookup -stoptb.van.id=@env.STOPTB_VAN_ID@ - cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@ #### SMS Configuration @@ -85,10 +81,14 @@ diagnostic.emrlite.truenat.password=@env.DIAGNOSTIC_EMRLITE_TRUENAT_PASSWORD@ diagnostic.emrlite.token-ttl-seconds=@env.DIAGNOSTIC_EMRLITE_TOKEN_TTL_SECONDS@ diagnostic.emrlite.refresh-ttl-seconds=@env.DIAGNOSTIC_EMRLITE_REFRESH_TTL_SECONDS@ - +diagnostic.documents.storage-root=@env.DIAGNOSTIC_DOCUMENTS_STORAGE_ROOT@ diagnostic.poll.xray.tick-ms=@env.DIAGNOSTIC_POLL_XRAY_TICK_MS@ diagnostic.poll.truenat.tick-ms=@env.DIAGNOSTIC_POLL_TRUENAT_TICK_MS@ # Daily cutoff (cron expression) after which all outstanding PENDING/IN_PROGRESS orders are marked EXPIRED diagnostic.poll.expiry-cron=@env.DIAGNOSTIC_POLL_EXPIRY_CRON@ -diagnostic.documents.storage-root=@env.DIAGNOSTIC_DOCUMENTS_STORAGE_ROOT@ +stoptb.enforce.vanid=@env.STOPTB_ENFORCE_VANID@ +# Stop TB: this deployment's van/camp ID, replacing the old Redis camp:vanID lookup +stoptb.van.id=@env.STOPTB_VAN_ID@ + + diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index b37a8e00e..e38175a51 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -1,5 +1,8 @@ fhir-url = ${FHIR_API} tm-url = ${TM_API} +common-api-base-url=${COMMON_API} + + ##--------------------------------------------## Primary db------------------------------------------------------------------- diff --git a/src/main/java/com/iemr/flw/config/SwaggerConfig.java b/src/main/java/com/iemr/flw/config/SwaggerConfig.java index 1052c2af7..f551de7c5 100644 --- a/src/main/java/com/iemr/flw/config/SwaggerConfig.java +++ b/src/main/java/com/iemr/flw/config/SwaggerConfig.java @@ -23,21 +23,35 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import java.util.List; @Configuration public class SwaggerConfig { + private static final String DEFAULT_SERVER_URL = "http://localhost:9090"; + private static final String BEARER_AUTH_SCHEME = "bearerAuth"; + @Bean - OpenAPI customOpenAPI() { - return new OpenAPI().info(new - Info().title("FLW API").version("version").description("A microservice for the creation and management of beneficiaries.")) - .addSecurityItem(new SecurityRequirement().addList("my security")) - .components(new Components().addSecuritySchemes("my security", - new SecurityScheme().name("my security").type(SecurityScheme.Type.HTTP).scheme("bearer"))); - } + public OpenAPI customOpenAPI(Environment env) { + String devUrl = env.getProperty("api.dev.url", DEFAULT_SERVER_URL); + String uatUrl = env.getProperty("api.uat.url", DEFAULT_SERVER_URL); + String demoUrl = env.getProperty("api.demo.url", DEFAULT_SERVER_URL); + return new OpenAPI() + .info(new Info().title("FLW API").version("3.1.0").description("A microservice for the creation and management of Field Level Workers (FLW) in the AMRIT ecosystem.")) + .addSecurityItem(new SecurityRequirement().addList(BEARER_AUTH_SCHEME)) + .components(new Components().addSecuritySchemes(BEARER_AUTH_SCHEME, + new SecurityScheme().name(BEARER_AUTH_SCHEME).type(SecurityScheme.Type.HTTP).scheme("bearer"))) + .servers(List.of( + new Server().url(devUrl).description("Dev"), + new Server().url(uatUrl).description("UAT"), + new Server().url(demoUrl).description("Demo") + )); + } } diff --git a/src/main/java/com/iemr/flw/controller/AdolescentHealthController.java b/src/main/java/com/iemr/flw/controller/AdolescentHealthController.java index 11cc5e577..3b7dfe9ae 100644 --- a/src/main/java/com/iemr/flw/controller/AdolescentHealthController.java +++ b/src/main/java/com/iemr/flw/controller/AdolescentHealthController.java @@ -1,5 +1,6 @@ package com.iemr.flw.controller; +import com.google.gson.Gson; import com.iemr.flw.domain.iemr.AdolescentHealth; import com.iemr.flw.dto.identity.GetBenRequestHandler; import com.iemr.flw.dto.iemr.AdolescentHealthDTO; @@ -8,10 +9,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; @@ -27,7 +25,7 @@ public class AdolescentHealthController { private AdolescentHealthService adolescentHealthService; @RequestMapping(value = "/saveAll", method = RequestMethod.POST, headers = "Authorization") - public ResponseEntity> saveAdolescentHealth(@RequestBody AdolescentHealthDTO adolescentHealthDTO) { + public ResponseEntity> saveAdolescentHealth(@RequestBody AdolescentHealthDTO adolescentHealthDTO,@RequestHeader(value = "JwtToken") String token) { Map response = new HashMap<>(); try { @@ -52,25 +50,61 @@ public ResponseEntity> saveAdolescentHealth(@RequestBody Ado } - @RequestMapping(value = "/getAll",method = RequestMethod.POST, headers = "Authorization") - public ResponseEntity> getAllAdolescentHealth(@RequestBody GetBenRequestHandler getBenRequestHandler) { - Map response = new HashMap<>(); + @RequestMapping( + value = "/getAll", + method = RequestMethod.POST, + headers = "Authorization" + ) + public ResponseEntity> getAllAdolescentHealth( + @RequestBody GetBenRequestHandler getBenRequestHandler) { + + Map response = new HashMap<>(); + long startTime = System.currentTimeMillis(); + logger.info("========================================"); + + logger.info("getAllAdolescentHealth API called"); + logger.info("========================================"); + try { - List resultList = adolescentHealthService.getAllAdolescentHealth(getBenRequestHandler); + + logger.info("getAllAdolescentHealth request: {}", + new Gson().toJson(getBenRequestHandler)); + + logger.debug("Fetching adolescent health records"); + + List resultList = + adolescentHealthService.getAllAdolescentHealth(getBenRequestHandler); + + int recordCount = resultList == null ? 0 : resultList.size(); + + logger.info( + "getAllAdolescentHealth: fetched {} records", + recordCount + ); if (resultList != null && !resultList.isEmpty()) { - response.put("statusCode",200); + response.put("statusCode", 200); response.put("data", resultList); } else { + logger.warn("getAllAdolescentHealth: no records returned"); + response.put("statusCode", 500); response.put("error", "Invalid/NULL request obj"); } } catch (Exception e) { - logger.error("Error in get data : " + e); - response.put("statusCode",500); - response.put("error","Error in get data : " + e); + // Logs the exception along with its full stack trace. + logger.error("getAllAdolescentHealth: failed to fetch records", e); + response.put("statusCode", 500); + response.put("error", "Unable to fetch adolescent health records"); + } finally { + logger.info( + "getAllAdolescentHealth completed: statusCode={}, durationMs={}", + response.get("statusCode"), + System.currentTimeMillis() - startTime + ); } + return ResponseEntity.ok(response); } } diff --git a/src/main/java/com/iemr/flw/controller/AshaSupervisorIncentiveApproval.java b/src/main/java/com/iemr/flw/controller/AshaSupervisorIncentiveApproval.java index 3f840f614..3905d2e6f 100644 --- a/src/main/java/com/iemr/flw/controller/AshaSupervisorIncentiveApproval.java +++ b/src/main/java/com/iemr/flw/controller/AshaSupervisorIncentiveApproval.java @@ -37,6 +37,7 @@ public ResponseEntity getAshaListByFacility( @RequestHeader(value = "JwtToken") String token) { try { + logger.info("getAshaListByFacility request{}",request.getFacilityId()); if (token == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("UNAUTHORIZED"); @@ -65,16 +66,34 @@ public ResponseEntity getAshaListByFacility( } + @Operation(summary = "Get comprehensive ASHA Supervisor dashboard with facilities, ASHAs, villages and incentive data") + @PostMapping("/getSubCenter") + public String getSupervisorSubCenter(@RequestBody AshaByFacilityRequestDTO request,@RequestHeader(value = "JwtToken") String token ) { + OutputResponse response = new OutputResponse(); + logger.info("dashboard request{}",request.getFacilityId()); + + try { + if(token!=null){ + String result = supervisorDashboardService.getSupervisorSubCenter(jwtUtil.extractUserId(token),request.getMonth(),request.getYear()); + response.setResponse(result); + } + } catch (Exception e) { + logger.error("getSupervisorDashboard failed: " + e.getMessage(), e); + response.setError(e); + } + return response.toString(); + } @Operation(summary = "Get comprehensive ASHA Supervisor dashboard with facilities, ASHAs, villages and incentive data") @PostMapping("/dashboard") public String getSupervisorDashboard(@RequestBody AshaByFacilityRequestDTO request,@RequestHeader(value = "JwtToken") String token ) { OutputResponse response = new OutputResponse(); + logger.info("dashboard request{}",request.getFacilityId()); try { if(token!=null){ - String result = supervisorDashboardService.getSupervisorDashboard(jwtUtil.extractUserId(token),request.getMonth(),request.getYear()); + String result = supervisorDashboardService.getSupervisorDashboard(jwtUtil.extractUserId(token),request.getMonth(),request.getYear(),request.getFacilityId()); response.setResponse(result); } 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/controller/IncentiveController.java b/src/main/java/com/iemr/flw/controller/IncentiveController.java index fe1d93ac2..a251327d2 100644 --- a/src/main/java/com/iemr/flw/controller/IncentiveController.java +++ b/src/main/java/com/iemr/flw/controller/IncentiveController.java @@ -1,5 +1,6 @@ package com.iemr.flw.controller; +import com.google.gson.Gson; import com.iemr.flw.dto.identity.GetBenRequestHandler; import com.iemr.flw.dto.iemr.*; import com.iemr.flw.service.IncentiveService; @@ -31,6 +32,9 @@ public class IncentiveController { @Autowired IncentiveService incentiveService; + private static final Gson gson = new Gson(); + + @Autowired private JwtUtil jwtUtil; @@ -91,8 +95,10 @@ public String getAllIncentivesByUserId(@RequestBody GetBenRequestHandler request try { if (requestDTO != null) { - logger.info("request object with timestamp : " + new Timestamp(System.currentTimeMillis()) + " " - + requestDTO); + + logger.info("Request object with timestamp: {} | Request JSON: {}", + new Timestamp(System.currentTimeMillis()), + gson.toJson(requestDTO)); String s = incentiveService.getAllIncentivesByUserId(requestDTO); //logger.info("User Incentive:" + s); if (s != null) @@ -112,14 +118,15 @@ public String getAllIncentivesByUserId(@RequestBody GetBenRequestHandler request @Operation(summary = "get high risk assessment data of all beneficiaries registered with given user id") @RequestMapping(value = {"/claimedIncentiveByUser"}, method = {RequestMethod.POST}) public String getAllIncentivesByUserIdGroupByCount(@RequestBody GetBenRequestHandler requestDTO, - @RequestHeader(value = "Authorization") String Authorization) { + @RequestHeader(value = "jwtToken") String jwtToken) { OutputResponse response = new OutputResponse(); try { if (requestDTO != null) { + Integer userId= jwtUtil.extractUserId(jwtToken); logger.info("request object with timestamp : " + new Timestamp(System.currentTimeMillis()) + " " + requestDTO); - String s = incentiveService.getAllIncentivesGroupedSummary(requestDTO); + String s = incentiveService.getAllIncentivesGroupedSummary(requestDTO,userId); logger.info("User Incentive:" + s); if (s != null) response.setResponse(s); @@ -199,7 +206,7 @@ public ResponseEntity updateApprovalStatus( request.getMonth(), request.getYear(), request.isClaimed(), - token + token,request.getIncentiveId() ); Map response = new HashMap<>(); diff --git a/src/main/java/com/iemr/flw/controller/MaternalHealthController.java b/src/main/java/com/iemr/flw/controller/MaternalHealthController.java index 9c8459daa..2e58d76ae 100644 --- a/src/main/java/com/iemr/flw/controller/MaternalHealthController.java +++ b/src/main/java/com/iemr/flw/controller/MaternalHealthController.java @@ -182,6 +182,7 @@ public ResponseEntity>> get logger.info("Request: " + requestDTO); List result = maternalHealthService.getANCCounselling(requestDTO); + logger.info("ANC Counselling result: {}", new Gson().toJson(result)); response.setStatusCode(200); response.setStatus("Success"); diff --git a/src/main/java/com/iemr/flw/controller/MicroBirthPlanController.java b/src/main/java/com/iemr/flw/controller/MicroBirthPlanController.java index c501b4054..a5fb0afa8 100644 --- a/src/main/java/com/iemr/flw/controller/MicroBirthPlanController.java +++ b/src/main/java/com/iemr/flw/controller/MicroBirthPlanController.java @@ -56,26 +56,21 @@ public ResponseEntity> createMicroBirthPlan(@RequestBody Mic @RequestMapping(value = "getAll", method = RequestMethod.GET) - public ResponseEntity> getAllMicroBirthPlans(@RequestParam("userId") Integer userId) { + public ResponseEntity> getAllMicroBirthPlans( + @RequestHeader(value = "JwtToken") String jwtToken) { Map response = new HashMap<>(); - - Map data = new HashMap<>(); try { - if (userId != null) { - data.put("userId", userId); - data.put("entries", service.getAllMicroBirthPlans(userId)); - response.put("data", data); - response.put("statusCode", 200); - response.put("status", "Success"); - } - + Integer userId = jwtUtil.extractUserId(jwtToken); + data.put("userId", userId); + data.put("entries", service.getAllMicroBirthPlans(userId)); + response.put("data", data); + response.put("statusCode", 200); + response.put("status", "Success"); } catch (Exception e) { response.put("statusCode", 500); response.put("errorMessage", e.getMessage()); - - } return ResponseEntity.ok(response); diff --git a/src/main/java/com/iemr/flw/controller/NotificationController.java b/src/main/java/com/iemr/flw/controller/NotificationController.java new file mode 100644 index 000000000..54d0443e4 --- /dev/null +++ b/src/main/java/com/iemr/flw/controller/NotificationController.java @@ -0,0 +1,160 @@ +package com.iemr.flw.controller; + +import com.iemr.flw.dto.iemr.NotificationListDTO; +import com.iemr.flw.dto.iemr.SendNotificationRequestDTO; +import com.iemr.flw.service.NotificationService; +import com.iemr.flw.utils.JwtUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/notification") +public class NotificationController { + + private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + @Autowired + private JwtUtil jwtUtil; + + @Autowired + private NotificationService notificationService; + + /** GET /notifications/{receiverUserId}?page=0&size=20 */ + @PostMapping("/list") + public Map listNotifications(@RequestHeader(value = "JwtToken") String token) { + Map response = new HashMap<>(); + try { + Integer receiverUserId = jwtUtil.extractUserId(token); + if (receiverUserId != null) { + NotificationListDTO notificationListDTO = + notificationService.getNotifications(receiverUserId); + + if (notificationListDTO != null) { + response.put("statusCode", 200); + response.put("data", notificationListDTO); + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in get data : " + e); + response.put("statusCode", 5000); + response.put("error", "Error in get data : " + e); + } + return response; + } + + /** GET /notifications/{receiverUserId}/unread-count */ + @GetMapping("/{receiverUserId}/unread-count") + public Map unreadCount(@PathVariable Integer receiverUserId) { + Map response = new HashMap<>(); + try { + + if (receiverUserId != null) { + Long unreadCount = notificationService.getUnreadCount(receiverUserId); + + if (unreadCount != null) { + response.put("statusCode", 200); + response.put("data", unreadCount); + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in get data : " + e); + response.put("statusCode", 5000); + response.put("error", "Error in get data : " + e); + } + return response; + } + + /** PUT /notifications/{notificationId}/read */ + @PutMapping("/{notificationId}/read") + public Map markAsRead(@PathVariable Long notificationId) { + Map response = new HashMap<>(); + try { + if (notificationId != null) { + boolean success = notificationService.markAsRead(notificationId); + + response.put("statusCode", 200); + response.put("data", success); + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in update data : " + e); + response.put("statusCode", 5000); + response.put("error", "Error in update data : " + e); + } + return response; + } + + /** POST /notifications/send */ + @PostMapping("/send") + public Map sendNotification(@RequestBody SendNotificationRequestDTO request) { + Map response = new HashMap<>(); + try { + if (request != null) { + String result = notificationService.sendNotification( + request.getAppType(), + request.getTopic(), + request.getTitle(), + request.getBody(), + request.getRedirect(), + request.getNotificationType(), + request.getReceiverId()); + + if (result != null) { + response.put("statusCode", 200); + response.put("data", result); + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in send notification : " + e); + response.put("statusCode", 5000); + response.put("error", "Error in send notification : " + e); + } + return response; + } + + /** PUT /notifications/{receiverUserId}/read-all */ + @PutMapping("/{receiverUserId}/read-all") + public Map markAllAsRead(@PathVariable Integer receiverUserId) { + Map response = new HashMap<>(); + try { + if (receiverUserId != null) { + int updatedCount = notificationService.markAllAsRead(receiverUserId); + + response.put("statusCode", 200); + response.put("data", updatedCount); + } else { + response.put("statusCode", 5000); + response.put("error", "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in update data : " + e); + response.put("statusCode", 5000); + response.put("error", "Error in update data : " + e); + } + return response; + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/controller/StopTBController.java b/src/main/java/com/iemr/flw/controller/StopTBController.java index 7f73d29e2..e9a38513c 100644 --- a/src/main/java/com/iemr/flw/controller/StopTBController.java +++ b/src/main/java/com/iemr/flw/controller/StopTBController.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.iemr.flw.service.StopTBPrescriptionService; import com.iemr.flw.service.StopTBService; import com.iemr.flw.utils.response.OutputResponse; import io.swagger.v3.oas.annotations.Operation; @@ -51,7 +52,7 @@ public String getAllGeneralExaminations(@RequestBody Map body) { if (raw == null) throw new Exception("providerServiceMapID is required"); Integer villageID = body.get("villageID") != null ? Integer.parseInt(body.get("villageID").toString()) : null; Map data = stopTBService.getAllGeneralExaminations(Integer.parseInt(raw.toString()), villageID); - response.setResponse(gson.toJson(data)); + response.setResponse(gson.toJsonTree(data)); } catch (Exception e) { logger.error("Error in getAllGeneralExaminations: " + e); response.setError(5000, "Error fetching general examinations: " + e.getMessage()); @@ -84,7 +85,7 @@ public String getAllNurseTBScreenings(@RequestBody Map body) { if (raw == null) throw new Exception("providerServiceMapID is required"); Integer villageID = body.get("villageID") != null ? Integer.parseInt(body.get("villageID").toString()) : null; Map data = stopTBService.getAllNurseTBScreenings(Integer.parseInt(raw.toString()), villageID); - response.setResponse(gson.toJson(data)); + response.setResponse(gson.toJsonTree(data)); } catch (Exception e) { logger.error("Error in getAllNurseTBScreenings: " + e); response.setError(5000, "Error fetching TB screenings: " + e.getMessage()); @@ -101,6 +102,10 @@ public String saveGeneralOpd(@RequestBody List> dataList) { try { List> result = stopTBService.saveGeneralOpd(dataList); response.setResponse(gson.toJson(result)); + } catch (StopTBPrescriptionService.InsufficientStockException e) { + // Nothing was saved; the app shows this and lets the nurse lower the quantity. + logger.warn("saveGeneralOpd rejected: " + e.getMessage()); + response.setError(5000, e.getMessage()); } catch (Exception e) { logger.error("Error in saveGeneralOpd: " + e); response.setError(5000, "Error saving general OPD: " + e.getMessage()); @@ -117,7 +122,7 @@ public String getAllGeneralOpd(@RequestBody Map body) { if (raw == null) throw new Exception("providerServiceMapID is required"); Integer villageID = body.get("villageID") != null ? Integer.parseInt(body.get("villageID").toString()) : null; Map data = stopTBService.getAllGeneralOpd(Integer.parseInt(raw.toString()), villageID); - response.setResponse(gson.toJson(data)); + response.setResponse(gson.toJsonTree(data)); } catch (Exception e) { logger.error("Error in getAllGeneralOpd: " + e); response.setError(5000, "Error fetching general OPD records: " + e.getMessage()); @@ -150,7 +155,7 @@ public String getAllDiagnostics(@RequestBody Map body) { if (raw == null) throw new Exception("providerServiceMapID is required"); Integer villageID = body.get("villageID") != null ? Integer.parseInt(body.get("villageID").toString()) : null; Map data = stopTBService.getAllDiagnostics(Integer.parseInt(raw.toString()), villageID); - response.setResponse(gson.toJson(data)); + response.setResponse(gson.toJsonTree(data)); } catch (Exception e) { logger.error("Error in getAllDiagnostics: " + e); response.setError(5000, "Error fetching diagnostics records: " + e.getMessage()); diff --git a/src/main/java/com/iemr/flw/controller/StopTBSyncController.java b/src/main/java/com/iemr/flw/controller/StopTBSyncController.java new file mode 100644 index 000000000..23201436f --- /dev/null +++ b/src/main/java/com/iemr/flw/controller/StopTBSyncController.java @@ -0,0 +1,77 @@ +package com.iemr.flw.controller; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.iemr.flw.service.StopTBSyncService; +import com.iemr.flw.utils.JwtUtil; +import com.iemr.flw.utils.response.OutputResponse; +import io.swagger.v3.oas.annotations.Operation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/stoptb/sync") +public class StopTBSyncController { + + private final Logger logger = LoggerFactory.getLogger(StopTBSyncController.class); + + private final Gson gson = new GsonBuilder().serializeNulls().create(); + + @Autowired + private StopTBSyncService stopTBSyncService; + + @Autowired + private JwtUtil jwtUtil; + + /** + * Headers: JwtToken (required; the user's role mappings decide the allowed service and villages). + * Body: { token: string|null, providerServiceMapID: int, villageIds: [int] (empty = all of the user's + * villages), types: [string], limit: int }. + * types: the record types the app applies (all when omitted); changing it requires a new snapshot. + * Response: { supported, serverId, snapshot, items: [{type, op, key, version, data}], nextToken, hasMore }. + * Call again with nextToken while hasMore is true. On a server without the change log (AMRIT-DB before V132) the + * response is { supported: false } and the app keeps using the getAll endpoints. + */ + @PostMapping("/changes") + @Operation(summary = "STOP-TB records changed since the token (first call: everything in the villages)") + public String getChanges(@RequestBody Map body, + @RequestHeader(value = "Authorization", required = false) String authorization, + @RequestHeader(value = "JwtToken", required = false) String jwtToken) { + OutputResponse response = new OutputResponse(); + try { + // The user decides which villages may be synced, so the JWT is required here even + // where the request filter would let a mobile request through without one. + if (jwtToken == null || jwtToken.isBlank()) throw new IllegalArgumentException("JwtToken header is required"); + Integer userId = jwtUtil.extractUserId(jwtToken); + Object token = body.get("token"); + Integer psm = body.get("providerServiceMapID") != null + ? Integer.parseInt(body.get("providerServiceMapID").toString()) : null; + List villageIds = new ArrayList<>(); + if (body.get("villageIds") instanceof List) { + for (Object v : (List) body.get("villageIds")) { + if (v != null) villageIds.add((int) Double.parseDouble(v.toString())); + } + } + List types = new ArrayList<>(); + if (body.get("types") instanceof List) { + for (Object t : (List) body.get("types")) { + if (t != null) types.add(t.toString()); + } + } + Integer limit = body.get("limit") != null ? (int) Double.parseDouble(body.get("limit").toString()) : null; + Map data = stopTBSyncService.getChanges( + userId, token != null ? token.toString() : null, psm, villageIds, types, limit, authorization); + response.setResponse(gson.toJson(data)); + } catch (Exception e) { + logger.error("Error in sync/changes: " + e); + response.setError(5000, "Error fetching changes: " + e.getMessage()); + } + return response.toString(); + } +} diff --git a/src/main/java/com/iemr/flw/controller/TBController.java b/src/main/java/com/iemr/flw/controller/TBController.java index becc387a9..9ec9f73a0 100644 --- a/src/main/java/com/iemr/flw/controller/TBController.java +++ b/src/main/java/com/iemr/flw/controller/TBController.java @@ -2,9 +2,7 @@ import com.iemr.flw.domain.iemr.TBConfirmedCaseDTO; import com.iemr.flw.dto.identity.GetBenRequestHandler; -import com.iemr.flw.dto.iemr.TBConfirmedRequestDTO; -import com.iemr.flw.dto.iemr.TBScreeningRequestDTO; -import com.iemr.flw.dto.iemr.TBSuspectedRequestDTO; +import com.iemr.flw.dto.iemr.*; import com.iemr.flw.service.TBConfirmedCaseService; import com.iemr.flw.service.TBScreeningService; import com.iemr.flw.service.TBSuspectedService; @@ -192,6 +190,140 @@ public String getConfirmedByBenId( return response.toString(); } + @Operation(summary = "save tb referral follow up") + @RequestMapping(value = { "/referralFollowUp/save" }, method = { RequestMethod.POST }) + public String saveReferralFollowUp( + @RequestBody TbReferralFollowUpDTO requestDTO, + @RequestHeader(value = "jwtToken") String token) { + + OutputResponse response = new OutputResponse(); + + try { + if (requestDTO != null) { + + logger.info("request object with timestamp : " + + new Timestamp(System.currentTimeMillis()) + " " + + requestDTO); + + String result = tbConfirmedCaseService.saveReferralFollowUp(requestDTO, token); + + if (result != null) + response.setResponse(result); + else + response.setError(500, "No record saved"); + + } else { + response.setError(500, "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in save tb referral follow up details : ", e); + response.setError(500, "Error in save referral follow up details : " + e.getMessage()); + } + + return response.toString(); + } + + + @Operation(summary = "save tb referral follow up") + @RequestMapping(value = { "/referralFollowUp/getAll" }, method = { RequestMethod.POST }) + public String getReferralFollowUp( + @RequestHeader(value = "jwtToken") String token) { + + OutputResponse response = new OutputResponse(); + + try { + if (token != null) { + + logger.info("request object with timestamp : " + + new Timestamp(System.currentTimeMillis()) + " " + + token); + + String result = tbConfirmedCaseService.getReferralFollowUp(token); + + if (result != null) + response.setResponse(result); + else + response.setError(500, "No record saved"); + + } else { + response.setError(500, "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in save tb referral follow up details : ", e); + response.setError(500, "Error in save referral follow up details : " + e.getMessage()); + } + + return response.toString(); + } + + + + @Operation(summary = "save tb tpt follow up") + @RequestMapping(value = { "/tptFollowUp/save" }, method = { RequestMethod.POST }) + public String savTptFollowUp( + @RequestBody TbTptFollowUpDTO requestDTO, + @RequestHeader(value = "jwtToken") String token) { + + OutputResponse response = new OutputResponse(); + + try { + if (requestDTO != null) { + + logger.info("request object with timestamp : " + + new Timestamp(System.currentTimeMillis()) + " " + + requestDTO); + + String result = tbConfirmedCaseService.saveTptFollowUp(requestDTO, token); + + if (result != null) + response.setResponse(result); + else + response.setError(500, "No record saved"); + + } else { + response.setError(500, "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in save tb tpt follow up details : ", e); + response.setError(500, "Error in save tpt follow up details : " + e.getMessage()); + } + + return response.toString(); + } + + + @Operation(summary = "save tb tpt follow up") + @RequestMapping(value = { "/tptFollowUp/getAll" }, method = { RequestMethod.POST }) + public String getTptFollowUp( + @RequestHeader(value = "jwtToken") String token) { + + OutputResponse response = new OutputResponse(); + + try { + if (token != null) { + + logger.info("request object with timestamp : " + + new Timestamp(System.currentTimeMillis()) + " " + + token); + + String result = tbConfirmedCaseService.getTptFollowUp(token); + + if (result != null) + response.setResponse(result); + else + response.setError(500, "No record saved"); + + } else { + response.setError(500, "Invalid/NULL request obj"); + } + } catch (Exception e) { + logger.error("Error in save tb tpt follow up details : ", e); + response.setError(500, "Error in save tpt follow up details : " + e.getMessage()); + } + + return response.toString(); + } + } diff --git a/src/main/java/com/iemr/flw/controller/UserController.java b/src/main/java/com/iemr/flw/controller/UserController.java index 605bc0ddf..2ae329227 100644 --- a/src/main/java/com/iemr/flw/controller/UserController.java +++ b/src/main/java/com/iemr/flw/controller/UserController.java @@ -3,13 +3,13 @@ import com.iemr.flw.dto.iemr.UserServiceRoleDTO; import com.iemr.flw.service.UserService; import com.iemr.flw.utils.ApiResponse; +import com.iemr.flw.utils.JwtUtil; import io.swagger.v3.oas.annotations.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -23,11 +23,15 @@ public class UserController { @Autowired private UserService userService; + @Autowired + private JwtUtil jwtUtil; + @Operation(summary = "get user Detail of userId and roleId") @RequestMapping(value = { "/getUserDetail" }, method = { RequestMethod.GET }) - public ResponseEntity getUserDetail(@RequestParam(value = "userId") Integer userId, - @RequestHeader(value = "Authorization") String Authorization) { + public ResponseEntity getUserDetail( + @RequestHeader(value = "JwtToken") String jwtToken) { try { + Integer userId = jwtUtil.extractUserId(jwtToken); UserServiceRoleDTO result = userService.getUserDetail(userId); return new ResponseEntity<>( new ApiResponse(true, null, result), HttpStatus.ACCEPTED); diff --git a/src/main/java/com/iemr/flw/domain/identity/RMNCHHouseHoldDetails.java b/src/main/java/com/iemr/flw/domain/identity/RMNCHHouseHoldDetails.java index 20cd8bfbf..c5cfa8524 100644 --- a/src/main/java/com/iemr/flw/domain/identity/RMNCHHouseHoldDetails.java +++ b/src/main/java/com/iemr/flw/domain/identity/RMNCHHouseHoldDetails.java @@ -11,331 +11,324 @@ @Data public class RMNCHHouseHoldDetails { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Expose - @Column(name = "houseHoldDetailsId", insertable = false, updatable = false) - private Long houseHoldDetailsId; - - @Expose - @Column(name = "BeneficiaryRegID") - private Long BenRegId; - - @Expose - @Column(name = "Countyid") - private Integer Countyid; - - @Expose - @Column(name = "availabilityofToilet") - private String availabilityofToilet; - - @Expose - @Column(name = "availabilityofToiletId") - private Integer availabilityofToiletId; - - @Expose - @Column(name = "avalabilityofElectricity") - private String avalabilityofElectricity; - - @Expose - @Column(name = "avalabilityofElectricityId") - private Integer avalabilityofElectricityId; - - @Expose - @Column(name = "block") - private String block; - - @Expose - @Column(name = "bpl_aplId") - private Integer bpl_aplId; - - @Expose - @Column(name = "colony") - private String colony; - - @Expose - @Column(name = "district") - private String district; - - @Expose - @Column(name = "districtid") - private Integer districtid; - - @Expose - @Column(name = "districtname") - private String districtname; - - @Expose - @Column(name = "familyHeadName") - private String familyHeadName; - - @Expose - @Column(name = "familyHeadPhoneNo") - private String familyHeadPhoneNo; - - @Expose - @Column(name = "familyName") - private String familyName; - - @Expose - @Column(name = "fuelUsed") - private String fuelUsed; - - @Expose - @Column(name = "fuelUsedId") - private Integer fuelUsedId; - - @Expose - @Column(name = "houseOwnerShip") - private String houseOwnerShip; - - @Expose - @Column(name = "houseOwnerShipId") - private Integer houseOwnerShipId; - - @Expose - @Column(name = "houseType") - private String houseType; - - @Expose - @Column(name = "houseTypeId") - private Integer houseTypeId; - - @Expose - @Column(name = "houseno") - private String houseno; - - @Expose - @Column(name = "houseNum") - private String houseNum; - @Expose - @Column(name = "houseoldId") - private Long houseoldId; - - @Expose - @Column(name = "landIrregatedId") - private Integer landIrregatedId; - - @Expose - @Column(name = "landOwnedId") - private Integer landOwnedId; - - @Expose - @Column(name = "liveStockOwnerShipId") - private Integer liveStockOwnerShipId; - - @Expose - @Column(name = "motarizedVehicleId") - private Integer motarizedVehicleId; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Expose + @Column(name = "houseHoldDetailsId", insertable = false, updatable = false) + private Long houseHoldDetailsId; + + @Expose + @Column(name = "BeneficiaryRegID") + private Long BenRegId; + + @Expose + @Column(name = "Countyid") + private Integer Countyid; + + @Expose + @Column(name = "availabilityofToilet") + private String availabilityofToilet; + + @Expose + @Column(name = "availabilityofToiletId") + private Integer availabilityofToiletId; + + @Expose + @Column(name = "avalabilityofElectricity") + private String avalabilityofElectricity; + + @Expose + @Column(name = "avalabilityofElectricityId") + private Integer avalabilityofElectricityId; + + @Expose + @Column(name = "block") + private String block; + + @Expose + @Column(name = "bpl_aplId") + private Integer bpl_aplId; + + @Expose + @Column(name = "colony") + private String colony; + + @Expose + @Column(name = "district") + private String district; + + @Expose + @Column(name = "districtid") + private Integer districtid; + + @Expose + @Column(name = "districtname") + private String districtname; + + @Expose + @Column(name = "familyHeadName") + private String familyHeadName; + + @Expose + @Column(name = "familyHeadPhoneNo") + private String familyHeadPhoneNo; + + @Expose + @Column(name = "familyName") + private String familyName; + + @Expose + @Column(name = "fuelUsed") + private String fuelUsed; + + @Expose + @Column(name = "fuelUsedId") + private Integer fuelUsedId; + + @Expose + @Column(name = "houseOwnerShip") + private String houseOwnerShip; + + @Expose + @Column(name = "houseOwnerShipId") + private Integer houseOwnerShipId; + + @Expose + @Column(name = "houseType") + private String houseType; + + @Expose + @Column(name = "houseTypeId") + private Integer houseTypeId; + + @Expose + @Column(name = "houseno") + private String houseno; + + @Expose + @Column(name = "houseNum") + private String houseNum; + @Expose + @Column(name = "houseoldId") + private Long houseoldId; + + @Expose + @Column(name = "landIrregatedId") + private Integer landIrregatedId; - @Expose - @Column(name = "other_availabilityofToilet") - private String other_availabilityofToilet; + @Expose + @Column(name = "landOwnedId") + private Integer landOwnedId; - @Expose - @Column(name = "other_avalabilityofElectricity") - private String other_avalabilityofElectricity; + @Expose + @Column(name = "liveStockOwnerShipId") + private Integer liveStockOwnerShipId; - @Expose - @Column(name = "other_fuelUsed") - private String other_fuelUsed; + @Expose + @Column(name = "motarizedVehicleId") + private Integer motarizedVehicleId; - @Expose - @Column(name = "other_houseType") - private String other_houseType; + @Expose + @Column(name = "other_availabilityofToilet") + private String other_availabilityofToilet; - @Expose - @Column(name = "other_motarizedVehicle") - private String other_motarizedVehicle; + @Expose + @Column(name = "other_avalabilityofElectricity") + private String other_avalabilityofElectricity; - @Expose - @Column(name = "other_residentialArea") - private String other_residentialArea; + @Expose + @Column(name = "other_fuelUsed") + private String other_fuelUsed; - @Expose - @Column(name = "other_sourceofDrinkingWater") - private String other_sourceofDrinkingWater; + @Expose + @Column(name = "other_houseType") + private String other_houseType; - @Expose - @Column(name = "pincode") - private Integer pincode; + @Expose + @Column(name = "other_motarizedVehicle") + private String other_motarizedVehicle; - @Expose - @Column(name = "rationCardDetails") - private String rationCardDetails; + @Expose + @Column(name = "other_residentialArea") + private String other_residentialArea; - @Expose - @Column(name = "residentialArea") - private String residentialArea; + @Expose + @Column(name = "other_sourceofDrinkingWater") + private String other_sourceofDrinkingWater; - @Expose - @Column(name = "residentialAreaId") - private Integer residentialAreaId; + @Expose + @Column(name = "pincode") + private Integer pincode; - @Expose - @Column(name = "seperateKitchen") - private String seperateKitchen; + @Expose + @Column(name = "rationCardDetails") + private String rationCardDetails; - @Expose - @Column(name = "seperateKitchenId") - private Integer seperateKitchenId; + @Expose + @Column(name = "residentialArea") + private String residentialArea; - @Expose - @Column(name = "serverUpdatedStatus") - private Integer serverUpdatedStatus; + @Expose + @Column(name = "residentialAreaId") + private Integer residentialAreaId; - @Expose - @Column(name = "sourceofDrinkingWater") - private String sourceofDrinkingWater; + @Expose + @Column(name = "seperateKitchen") + private String seperateKitchen; - @Expose - @Column(name = "sourceofDrinkingWaterId") - private Integer sourceofDrinkingWaterId; + @Expose + @Column(name = "seperateKitchenId") + private Integer seperateKitchenId; - @Expose - @Column(name = "state") - private String state; + @Expose + @Column(name = "serverUpdatedStatus") + private Integer serverUpdatedStatus; - @Expose - @Column(name = "stateid") - private Integer stateid; + @Expose + @Column(name = "sourceofDrinkingWater") + private String sourceofDrinkingWater; - @Expose - @Column(name = "street") - private String street; + @Expose + @Column(name = "sourceofDrinkingWaterId") + private Integer sourceofDrinkingWaterId; - @Expose - @Column(name = "type_bpl_apl") - private String type_bpl_apl; + @Expose + @Column(name = "state") + private String state; - @Expose - @Column(name = "village") - private String village; + @Expose + @Column(name = "stateid") + private Integer stateid; - @Expose - @Column(name = "villageid") - private Integer villageid; + @Expose + @Column(name = "street") + private String street; - @Expose - @Column(name = "Deleted") - private Boolean deleted; + @Expose + @Column(name = "type_bpl_apl") + private String type_bpl_apl; - @Expose - @Column(name = "Processed") - private String Processed = "N"; + @Expose + @Column(name = "village") + private String village; - @Expose - @Column(name = "CreatedBy") - private String createdBy; + @Expose + @Column(name = "villageid") + private Integer villageid; - @Expose - @Column(name = "CreatedDate") - private Timestamp createdDate; + @Expose + @Column(name = "Deleted") + private Boolean deleted; - @Expose - @Column(name = "Reserved") - private Boolean reserved; + @Expose + @Column(name = "Processed") + private String Processed = "N"; + + @Expose + @Column(name = "CreatedBy") + private String createdBy; + + @Expose + @Column(name = "CreatedDate") + private Timestamp createdDate; - @Expose - @Column(name = "ReservedFor") - private String reservedFor; + @Expose + @Column(name = "Reserved") + private Boolean reserved; - @Expose - @Column(name = "ReservedOn") - private String reservedOn; + @Expose + @Column(name = "ReservedFor") + private String reservedFor; - @Expose - @Column(name = "ReservedById") - private Integer reservedById; + @Expose + @Column(name = "ReservedOn") + private String reservedOn; - @Expose - @Column(name = "ModifiedBy") - private String modifiedBy; + @Expose + @Column(name = "ReservedById") + private Integer reservedById; - @Expose - @Column(name = "LastModDate") - private Timestamp lastModDate; + @Expose + @Column(name = "ModifiedBy") + private String modifiedBy; - @Expose - @Column(name = "VanSerialNo") - private Long id; + @Expose + @Column(name = "LastModDate") + private Timestamp lastModDate; - @Expose - @Column(name = "VanID") - private Integer VanID; + @Expose + @Column(name = "VanSerialNo") + private Long id; - @Expose - @Column(name = "VehicalNo") - private String vehicalNo; + @Expose + @Column(name = "VanID") + private Integer VanID; - @Expose - @Column(name = "ParkingPlaceID") - private Integer parkingPlaceID; + @Expose + @Column(name = "VehicalNo") + private String vehicalNo; - @Expose - @Column(name = "SyncedBy") - private String syncedBy; + @Expose + @Column(name = "ParkingPlaceID") + private Integer parkingPlaceID; - @Expose - @Column(name = "SyncedDate") - private Timestamp syncedDate; + @Expose + @Column(name = "SyncedBy") + private String syncedBy; - @Expose - @Column(name = "ProviderServiceMapID") - private Integer ProviderServiceMapID; + @Expose + @Column(name = "SyncedDate") + private Timestamp syncedDate; - @Expose - @Column(name = "deviceId") - private Integer deviceId; + @Expose + @Column(name = "ProviderServiceMapID") + private Integer ProviderServiceMapID; - @Expose - @Column(name = "beneficiaryId") - private Integer beneficiaryId; + @Expose + @Column(name = "deviceId") + private Integer deviceId; - // new fields, 30-06-2021 + @Expose + @Column(name = "beneficiaryId") + private Integer beneficiaryId; - @Expose - @Column(name = "MotarizedVehicle") - private String motarizedVehicle; + // new fields, 30-06-2021 - @Expose - @Column(name = "LiveStockOwnerShip") - private String liveStockOwnerShip; + @Expose + @Column(name = "MotarizedVehicle") + private String motarizedVehicle; - @Expose - @Column(name = "LandOwned") - private String landOwned; + @Expose + @Column(name = "LiveStockOwnerShip") + private String liveStockOwnerShip; - @Expose - @Column(name = "Blockid") - private Integer blockid; + @Expose + @Column(name = "LandOwned") + private String landOwned; - @Expose - @Column(name = "landIrregated") - private String landIrregated; + @Expose + @Column(name = "Blockid") + private Integer blockid; - // 19-09-2021 - @Expose - @Column(name = "wardNo") - private String wardNo; - @Expose - @Column(name = "wardName") - private String wardName; - @Expose - @Column(name = "mohallaName") - private String mohallaName; + @Expose + @Column(name = "landIrregated") + private String landIrregated; - @Expose - @Column(name = "isDeactivate") - private Boolean isDeactivate; + // 19-09-2021 + @Expose + @Column(name = "wardNo") + private String wardNo; + @Expose + @Column(name = "wardName") + private String wardName; + @Expose + @Column(name = "mohallaName") + private String mohallaName; - @Expose - @Column(name = "gpsLatitude") - private Double gpsLatitude; + @Expose + @Column(name = "isDeactivate") + private Boolean isDeactivate; - @Expose - @Column(name = "gpsLongitude") - private Double gpsLongitude; @Expose @Column(name = "address") @@ -353,4 +346,12 @@ public class RMNCHHouseHoldDetails { @Column(name = "registeredAtCampSiteId") private Integer registeredAtCampSiteId; + @Expose + @Column(name = "placeOfCurrentLiving") + private String placeOfCurrentLiving; + + @Expose + @Column(name = "nameOfInstitution") + private String nameOfInstitution; + } 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 a9a20dfb6..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; @@ -44,4 +46,11 @@ public class AHDForm { @Column(name = "form_type") private String formType; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/ANCVisit.java b/src/main/java/com/iemr/flw/domain/iemr/ANCVisit.java index 4128f0826..c39047f73 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ANCVisit.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ANCVisit.java @@ -173,7 +173,12 @@ public class ANCVisit { @Column(name = "place_of_ancId") private Integer placeOfAncId; + @Column(name = "synced_by") + private String syncedBy; + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/AdolescentHealth.java b/src/main/java/com/iemr/flw/domain/iemr/AdolescentHealth.java index 140087069..3aa0ac4b7 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/AdolescentHealth.java +++ b/src/main/java/com/iemr/flw/domain/iemr/AdolescentHealth.java @@ -68,5 +68,12 @@ public class AdolescentHealth { @Column(name = "created_by") private String createdBy; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + } diff --git a/src/main/java/com/iemr/flw/domain/iemr/AncCare.java b/src/main/java/com/iemr/flw/domain/iemr/AncCare.java index 6b1d5c752..63da1f0f2 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/AncCare.java +++ b/src/main/java/com/iemr/flw/domain/iemr/AncCare.java @@ -1,10 +1,13 @@ package com.iemr.flw.domain.iemr; import jakarta.persistence.*; +import lombok.Data; + import java.sql.Timestamp; import java.util.Objects; @Entity +@Data @Table(name = "t_anccare", schema = "db_iemr") public class AncCare { private long id; @@ -41,6 +44,7 @@ public class AncCare { private Timestamp syncedDate; private String reservedForChange; + @Id @Column(name = "ID") public long getId() { diff --git a/src/main/java/com/iemr/flw/domain/iemr/AncCounsellingCare.java b/src/main/java/com/iemr/flw/domain/iemr/AncCounsellingCare.java index 46737f12a..e80fb13a5 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/AncCounsellingCare.java +++ b/src/main/java/com/iemr/flw/domain/iemr/AncCounsellingCare.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.Data; +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; @@ -13,6 +14,7 @@ public class AncCounsellingCare { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") private Long id; @Column(name = "beneficiary_id", nullable = false) @@ -25,7 +27,7 @@ public class AncCounsellingCare { private LocalDate homeVisitDate; @Column(name = "anc_visit_id", nullable = false) - private Long ancVisitId; + private Long ancVisitId=0L; @Column(name = "user_id") private Integer userId; @@ -107,9 +109,17 @@ public class AncCounsellingCare { @Column(name = "prolonged_labor") private Boolean prolongedLabor = false; + @Column(name = "malpresentation") private Boolean malpresentation = false; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + /* ---------- Lifecycle Hooks ---------- */ @PrePersist diff --git a/src/main/java/com/iemr/flw/domain/iemr/AshaSupervisorMapping.java b/src/main/java/com/iemr/flw/domain/iemr/AshaSupervisorMapping.java index ee641db8b..1817d8dee 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/AshaSupervisorMapping.java +++ b/src/main/java/com/iemr/flw/domain/iemr/AshaSupervisorMapping.java @@ -9,6 +9,8 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.sql.Timestamp; + @Entity @Table(name = "asha_supervisor_mapping") @Data @@ -31,4 +33,11 @@ public class AshaSupervisorMapping { @Column(name = "deleted", insertable = false, updatable = true) private Boolean deleted; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/AshaWorker.java b/src/main/java/com/iemr/flw/domain/iemr/AshaWorker.java index 2df2bf43c..0c3e3b928 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/AshaWorker.java +++ b/src/main/java/com/iemr/flw/domain/iemr/AshaWorker.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.*; import lombok.*; +import java.sql.Timestamp; import java.time.LocalDate; @@ -109,4 +110,11 @@ public class AshaWorker { @Column(name = "supervisorMobile") private String supervisorMobile; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + } 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..382113f94 --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/BadgeEarned.java @@ -0,0 +1,62 @@ +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. + * + * 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_key", + columnNames = {"user_id", "badge_id", "level", "award_key"})) +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; + + /** + * 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; + + /** 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/domain/iemr/BenAnthropometryDetail.java b/src/main/java/com/iemr/flw/domain/iemr/BenAnthropometryDetail.java index 777e80118..f4e23e6d8 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/BenAnthropometryDetail.java +++ b/src/main/java/com/iemr/flw/domain/iemr/BenAnthropometryDetail.java @@ -65,6 +65,8 @@ public class BenAnthropometryDetail { @Column(name = "SyncedBy") private String syncedBy; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; + } diff --git a/src/main/java/com/iemr/flw/domain/iemr/BenChiefComplaint.java b/src/main/java/com/iemr/flw/domain/iemr/BenChiefComplaint.java index 3b016c5fc..964f73c3c 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/BenChiefComplaint.java +++ b/src/main/java/com/iemr/flw/domain/iemr/BenChiefComplaint.java @@ -68,6 +68,7 @@ public class BenChiefComplaint { @Column(name = "SyncedBy") private String syncedBy; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/BenFlowStatus.java b/src/main/java/com/iemr/flw/domain/iemr/BenFlowStatus.java index b4600e265..3451414a7 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/BenFlowStatus.java +++ b/src/main/java/com/iemr/flw/domain/iemr/BenFlowStatus.java @@ -79,4 +79,11 @@ public class BenFlowStatus { @Column(name = "deleted", insertable = false) private Boolean deleted; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/BenPhysicalVitalDetail.java b/src/main/java/com/iemr/flw/domain/iemr/BenPhysicalVitalDetail.java index 15b03aee4..328877a01 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/BenPhysicalVitalDetail.java +++ b/src/main/java/com/iemr/flw/domain/iemr/BenPhysicalVitalDetail.java @@ -71,6 +71,7 @@ public class BenPhysicalVitalDetail { @Column(name = "SyncedBy") private String syncedBy; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/BenReferDetails.java b/src/main/java/com/iemr/flw/domain/iemr/BenReferDetails.java index 2112f3110..061aa9d9f 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/BenReferDetails.java +++ b/src/main/java/com/iemr/flw/domain/iemr/BenReferDetails.java @@ -126,6 +126,7 @@ public class BenReferDetails { private String syncedBy; @Expose + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/CDR.java b/src/main/java/com/iemr/flw/domain/iemr/CDR.java index 89744928c..e3e028dee 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/CDR.java +++ b/src/main/java/com/iemr/flw/domain/iemr/CDR.java @@ -93,4 +93,11 @@ public class CDR { @Column(name = "updated_by") private String updatedBy; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + } diff --git a/src/main/java/com/iemr/flw/domain/iemr/CampaignOrs.java b/src/main/java/com/iemr/flw/domain/iemr/CampaignOrs.java index e33e15439..ec032f84a 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/CampaignOrs.java +++ b/src/main/java/com/iemr/flw/domain/iemr/CampaignOrs.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.Data; +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; @@ -42,6 +43,13 @@ public class CampaignOrs { @Column(name = "updated_date") private LocalDateTime updatedDate; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + /* ---------- Auto timestamps ---------- */ @PrePersist diff --git a/src/main/java/com/iemr/flw/domain/iemr/CbacDetailsImer.java b/src/main/java/com/iemr/flw/domain/iemr/CbacDetailsImer.java index 989ed1d1c..d46335ef9 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/CbacDetailsImer.java +++ b/src/main/java/com/iemr/flw/domain/iemr/CbacDetailsImer.java @@ -262,6 +262,7 @@ public class CbacDetailsImer { @Column(name = "SyncedBy") private String syncedBy; @Expose + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/ChildRegister.java b/src/main/java/com/iemr/flw/domain/iemr/ChildRegister.java index 4b4ca99aa..c4cbef8d4 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ChildRegister.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ChildRegister.java @@ -75,4 +75,11 @@ public class ChildRegister { @Column(name = "updated_by") private String updatedBy; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/ChronicDiseaseVisitEntity.java b/src/main/java/com/iemr/flw/domain/iemr/ChronicDiseaseVisitEntity.java index 10689cff3..f6b42581c 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ChronicDiseaseVisitEntity.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ChronicDiseaseVisitEntity.java @@ -2,6 +2,8 @@ import jakarta.persistence.*; import lombok.Data; + +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; @@ -59,6 +61,13 @@ public class ChronicDiseaseVisitEntity { @Column(name = "updated_date") private LocalDateTime updatedDate; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + // 🔹 Auto timestamps @PrePersist protected void onCreate() { diff --git a/src/main/java/com/iemr/flw/domain/iemr/DeliveryOutcome.java b/src/main/java/com/iemr/flw/domain/iemr/DeliveryOutcome.java index d3a1b56ce..d6e6868b1 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DeliveryOutcome.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DeliveryOutcome.java @@ -77,4 +77,11 @@ public class DeliveryOutcome { @Column(name = "updated_date") private Timestamp updatedDate; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + } 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 631dbfd51..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; @@ -47,4 +49,11 @@ public class DewormingForm { @Column(name = "form_type") private String formType; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticDocument.java b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticDocument.java index d4e78747f..8d8ecff25 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticDocument.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticDocument.java @@ -84,6 +84,13 @@ public class DiagnosticDocument { @Column(name = "parkingPlaceID") private Integer parkingPlaceID; - @Column(name = "vanSerialNo") + @Column(name = "vanSerialNo", updatable = false) private Long vanSerialNo; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file 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 f8f69e037..5ea9a4589 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java @@ -106,6 +106,13 @@ public class DiagnosticOrder { @Column(name = "processed") private String processed = "N"; - @Column(name = "vanSerialNo") + @Column(name = "vanSerialNo", updatable = false) private Long vanSerialNo; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticProviderToken.java b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticProviderToken.java index 8ef233060..198667f38 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticProviderToken.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticProviderToken.java @@ -46,6 +46,13 @@ public class DiagnosticProviderToken { @Column(name = "parkingPlaceID") private Integer parkingPlaceID; - @Column(name = "vanSerialNo") + @Column(name = "vanSerialNo", updatable = false) private Long vanSerialNo; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticResult.java b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticResult.java index 6cb9fba6b..55e5a0ca0 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DiagnosticResult.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DiagnosticResult.java @@ -70,6 +70,13 @@ public class DiagnosticResult { @Column(name = "parkingPlaceID") private Integer parkingPlaceID; - @Column(name = "vanSerialNo") + @Column(name = "vanSerialNo", updatable = false) private Long vanSerialNo; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/DynamicForm.java b/src/main/java/com/iemr/flw/domain/iemr/DynamicForm.java index af183d66c..1c9a30530 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/DynamicForm.java +++ b/src/main/java/com/iemr/flw/domain/iemr/DynamicForm.java @@ -106,6 +106,7 @@ public class DynamicForm { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleRegister.java b/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleRegister.java index 4a6226b44..b6299f903 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleRegister.java +++ b/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleRegister.java @@ -179,5 +179,12 @@ public class EligibleCoupleRegister { @Column(name = "kit_photo2", columnDefinition = "LONGTEXT") private String kitPhoto2; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleTracking.java b/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleTracking.java index 99b8b5115..929fc93bf 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleTracking.java +++ b/src/main/java/com/iemr/flw/domain/iemr/EligibleCoupleTracking.java @@ -72,4 +72,11 @@ public class EligibleCoupleTracking { @Column(name = "discharge_summary2") private String dischargeSummary2; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/EyeCheckupVisit.java b/src/main/java/com/iemr/flw/domain/iemr/EyeCheckupVisit.java index c15e84101..945fdeb48 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/EyeCheckupVisit.java +++ b/src/main/java/com/iemr/flw/domain/iemr/EyeCheckupVisit.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.Data; +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; @Data @@ -51,4 +52,11 @@ public class EyeCheckupVisit { @Column(name = "updated_date") private LocalDateTime updatedDate = LocalDateTime.now(); + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/FilariasisCampaign.java b/src/main/java/com/iemr/flw/domain/iemr/FilariasisCampaign.java index 2f0c12ad1..d2cd218a7 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/FilariasisCampaign.java +++ b/src/main/java/com/iemr/flw/domain/iemr/FilariasisCampaign.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.Data; +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; @@ -49,6 +50,13 @@ public class FilariasisCampaign { @Column(name = "updated_date") private LocalDateTime updatedDate; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + /* ---------- Auto timestamps ---------- */ @PrePersist diff --git a/src/main/java/com/iemr/flw/domain/iemr/FormResponse.java b/src/main/java/com/iemr/flw/domain/iemr/FormResponse.java index 8b338ee54..c6c160082 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/FormResponse.java +++ b/src/main/java/com/iemr/flw/domain/iemr/FormResponse.java @@ -112,6 +112,7 @@ public class FormResponse { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; @@ -121,6 +122,7 @@ public class FormResponse { @Column(name = "SyncFailureReason") private String syncFailureReason; + @PrePersist protected void onCreate() { createdAt = new Timestamp(System.currentTimeMillis()); diff --git a/src/main/java/com/iemr/flw/domain/iemr/FormSection.java b/src/main/java/com/iemr/flw/domain/iemr/FormSection.java index b645c9742..0f5e687ee 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/FormSection.java +++ b/src/main/java/com/iemr/flw/domain/iemr/FormSection.java @@ -117,6 +117,7 @@ public class FormSection { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/FormVersion.java b/src/main/java/com/iemr/flw/domain/iemr/FormVersion.java index 62a5f646b..1f26da77d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/FormVersion.java +++ b/src/main/java/com/iemr/flw/domain/iemr/FormVersion.java @@ -104,6 +104,7 @@ public class FormVersion { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdData.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdData.java index f2d81f185..b2b0f2d02 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdData.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdData.java @@ -220,5 +220,11 @@ public class GeneralOpdData { @Column(name = "village") private String village; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdEntry.java b/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdEntry.java index 2e68c9fe9..9254dab88 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdEntry.java +++ b/src/main/java/com/iemr/flw/domain/iemr/GeneralOpdEntry.java @@ -3,6 +3,8 @@ import jakarta.persistence.*; import lombok.Data; +import java.sql.Timestamp; + @Entity @Data @Table(name = "general_opd_entry") // Updated table name @@ -42,4 +44,11 @@ public class GeneralOpdEntry { @Column(name = "follow_up_date") private String followUpDate; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/HBYC.java b/src/main/java/com/iemr/flw/domain/iemr/HBYC.java index 2b5b1a08c..a52852a27 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/HBYC.java +++ b/src/main/java/com/iemr/flw/domain/iemr/HBYC.java @@ -102,4 +102,11 @@ public class HBYC { @Column(name = "updated_by") private String updatedBy; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/HbncPart1.java b/src/main/java/com/iemr/flw/domain/iemr/HbncPart1.java index f4ff6a5cd..02cb6736d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/HbncPart1.java +++ b/src/main/java/com/iemr/flw/domain/iemr/HbncPart1.java @@ -97,4 +97,11 @@ public class HbncPart1 { @Column(name = "updated_by") private String updatedBy; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/HbncPart2.java b/src/main/java/com/iemr/flw/domain/iemr/HbncPart2.java index b7828719f..7139086c5 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/HbncPart2.java +++ b/src/main/java/com/iemr/flw/domain/iemr/HbncPart2.java @@ -82,4 +82,11 @@ public class HbncPart2 { @Column(name = "updated_by") private String updatedBy; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/HbncVisit.java b/src/main/java/com/iemr/flw/domain/iemr/HbncVisit.java index 1513c0888..aa797a12c 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/HbncVisit.java +++ b/src/main/java/com/iemr/flw/domain/iemr/HbncVisit.java @@ -107,4 +107,14 @@ public class HbncVisit { @Column(name = "baby_eyes_swollen") private Boolean babyEyesSwollen; + @Column(name = "is_admitted_in_sncu") + private Boolean is_admitted_in_sncu; + + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; }; diff --git a/src/main/java/com/iemr/flw/domain/iemr/HbncVisitCard.java b/src/main/java/com/iemr/flw/domain/iemr/HbncVisitCard.java index 8008d07d5..c01d445f8 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/HbncVisitCard.java +++ b/src/main/java/com/iemr/flw/domain/iemr/HbncVisitCard.java @@ -79,4 +79,11 @@ public class HbncVisitCard { @Column(name = "updated_by") private String updatedBy; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/HbycChildVisit.java b/src/main/java/com/iemr/flw/domain/iemr/HbycChildVisit.java index 205716cc9..03b92da4f 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/HbycChildVisit.java +++ b/src/main/java/com/iemr/flw/domain/iemr/HbycChildVisit.java @@ -130,5 +130,12 @@ public class HbycChildVisit { @Column(name = "is_complementary_feeding") private Boolean complementary_feeding; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private java.sql.Timestamp syncedDate; + // Getters and Setters } diff --git a/src/main/java/com/iemr/flw/domain/iemr/HighRiskAssess.java b/src/main/java/com/iemr/flw/domain/iemr/HighRiskAssess.java index 4089e77c8..aac6f2df6 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/HighRiskAssess.java +++ b/src/main/java/com/iemr/flw/domain/iemr/HighRiskAssess.java @@ -45,4 +45,10 @@ public class HighRiskAssess { @Column(name = "updated_by") private String updatedBy; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/IFAFormSubmissionData.java b/src/main/java/com/iemr/flw/domain/iemr/IFAFormSubmissionData.java index 428269028..cf8e9e10d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/IFAFormSubmissionData.java +++ b/src/main/java/com/iemr/flw/domain/iemr/IFAFormSubmissionData.java @@ -62,4 +62,11 @@ public void onUpdate() { this.updatedAt = LocalDateTime.now(); } + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private java.sql.Timestamp syncedDate; + } diff --git a/src/main/java/com/iemr/flw/domain/iemr/IRSRound.java b/src/main/java/com/iemr/flw/domain/iemr/IRSRound.java index 6f3143e05..c964765ee 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/IRSRound.java +++ b/src/main/java/com/iemr/flw/domain/iemr/IRSRound.java @@ -7,6 +7,7 @@ import java.math.BigInteger; import java.sql.Date; +import java.sql.Timestamp; import java.time.LocalDate; @Entity @@ -34,4 +35,10 @@ public class IRSRound { @Column(name = "user_id") private Integer userId; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate = null; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/IfaDistribution.java b/src/main/java/com/iemr/flw/domain/iemr/IfaDistribution.java index c6b613549..9cbe52062 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/IfaDistribution.java +++ b/src/main/java/com/iemr/flw/domain/iemr/IfaDistribution.java @@ -5,6 +5,7 @@ import lombok.Data; import org.checkerframework.checker.units.qual.C; +import java.sql.Timestamp; import java.time.LocalDate; @Entity @@ -39,4 +40,11 @@ public class IfaDistribution { @Column(name = "ifa_bottle_count") private String ifaBottleCount; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivity.java b/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivity.java index 016f6271d..ce8ebc02e 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivity.java +++ b/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivity.java @@ -57,9 +57,19 @@ public class IncentiveActivity { @Column(name = "is_deleted") private Boolean isDeleted; - @Column(name = "state_activity_code") private Integer stateActivityCode; + @Column(name = "is_default_activity") + private Boolean isDefaultActivity; + + @Column(name = "group_category_name") + private String groupCategoryName; + + @Column(name = "synced_by") + private String syncedBy; + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityLangMapping.java b/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityLangMapping.java index 0c81338c1..f3ce1c2a7 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityLangMapping.java +++ b/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityLangMapping.java @@ -43,5 +43,10 @@ public class IncentiveActivityLangMapping { @Column(name = "hindi_activity_description") private String hindiActivityDescription; + @Column(name = "synced_by") + private String syncedBy; + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityRecord.java b/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityRecord.java index b6d193e63..c9b16da46 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityRecord.java +++ b/src/main/java/com/iemr/flw/domain/iemr/IncentiveActivityRecord.java @@ -87,4 +87,24 @@ public class IncentiveActivityRecord { @Transient private String groupName; + + @Transient String groupCategoryName; + + @Column(name = "is_approved") + private Boolean isApproved = false; + + @PrePersist + @PreUpdate + private void applyDefaults() { + if (isApproved == null) { + isApproved = false; + } + } + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/IncentivePendingActivity.java b/src/main/java/com/iemr/flw/domain/iemr/IncentivePendingActivity.java index 3f35dc599..b302b37b1 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/IncentivePendingActivity.java +++ b/src/main/java/com/iemr/flw/domain/iemr/IncentivePendingActivity.java @@ -1,5 +1,6 @@ package com.iemr.flw.domain.iemr; +import java.sql.Timestamp; import java.util.Date; import jakarta.persistence.*; @@ -36,4 +37,11 @@ public class IncentivePendingActivity { @Column(name = "updated_date") private Date updatedDate; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/InfantRegister.java b/src/main/java/com/iemr/flw/domain/iemr/InfantRegister.java index 7a22516fa..9c09c3472 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/InfantRegister.java +++ b/src/main/java/com/iemr/flw/domain/iemr/InfantRegister.java @@ -100,4 +100,14 @@ public class InfantRegister { @Column(name = "is_sncu") private String isSNCU; + + @Column(name = "is_Referred_higher_facility") + private Boolean isReferredHigherFacility; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/LeprosyFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/LeprosyFollowUp.java index 5e0d78f06..5decbe777 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/LeprosyFollowUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/LeprosyFollowUp.java @@ -87,4 +87,11 @@ public class LeprosyFollowUp { @Column(name = "last_mod_date") private Timestamp lastModDate; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/MDSR.java b/src/main/java/com/iemr/flw/domain/iemr/MDSR.java index 301fdd915..cace87f8c 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/MDSR.java +++ b/src/main/java/com/iemr/flw/domain/iemr/MDSR.java @@ -63,4 +63,11 @@ public class MDSR { @Column(name = "mdsr_death_cert_file") private String mdsrDeathCertFile; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/MaaMeeting.java b/src/main/java/com/iemr/flw/domain/iemr/MaaMeeting.java index 0e8437141..3381d59d8 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/MaaMeeting.java +++ b/src/main/java/com/iemr/flw/domain/iemr/MaaMeeting.java @@ -54,4 +54,11 @@ public class MaaMeeting { @Column(name = "created_by") private String createdBy; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/MalariaFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/MalariaFollowUp.java index bc30e7dd4..115c371e8 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/MalariaFollowUp.java +++ b/src/main/java/com/iemr/flw/domain/iemr/MalariaFollowUp.java @@ -56,4 +56,11 @@ public class MalariaFollowUp { @UpdateTimestamp @Column(name = "last_mod_date") private Timestamp lastModDate; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/MdaDistributionData.java b/src/main/java/com/iemr/flw/domain/iemr/MdaDistributionData.java index 34c7a63e4..b59164e3d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/MdaDistributionData.java +++ b/src/main/java/com/iemr/flw/domain/iemr/MdaDistributionData.java @@ -57,4 +57,14 @@ public class MdaDistributionData { @Column(name = "LastModDate") private Timestamp lastModDate; + + @Column(name = "AshaId") + private Integer ashaId; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/MicroBirthPlan.java b/src/main/java/com/iemr/flw/domain/iemr/MicroBirthPlan.java index 9b1458994..f5a89eccf 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/MicroBirthPlan.java +++ b/src/main/java/com/iemr/flw/domain/iemr/MicroBirthPlan.java @@ -66,4 +66,10 @@ public class MicroBirthPlan { private String modeOfTransportation; // Mode of transport -} \ No newline at end of file + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private java.sql.Timestamp syncedDate; +} diff --git a/src/main/java/com/iemr/flw/domain/iemr/MosquitoNetEntity.java b/src/main/java/com/iemr/flw/domain/iemr/MosquitoNetEntity.java index 1f57d23ae..e75a3b0e6 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/MosquitoNetEntity.java +++ b/src/main/java/com/iemr/flw/domain/iemr/MosquitoNetEntity.java @@ -31,4 +31,10 @@ public class MosquitoNetEntity { @Column(name = "is_net_distributed") private String isNetDistributed; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private java.sql.Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskAssess.java b/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskAssess.java index 2b901a6d3..863358b6d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskAssess.java +++ b/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskAssess.java @@ -50,4 +50,10 @@ public class NonPregnantWomanHighRiskAssess { @Column(name = "visit_date") private Timestamp visitDate; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskTrack.java b/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskTrack.java index 6a3374d02..9f506a9cd 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskTrack.java +++ b/src/main/java/com/iemr/flw/domain/iemr/NonPregnantWomanHighRiskTrack.java @@ -77,4 +77,11 @@ public class NonPregnantWomanHighRiskTrack { @Column(name = "is_deleted") private Boolean isDeleted; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/Notification.java b/src/main/java/com/iemr/flw/domain/iemr/Notification.java new file mode 100644 index 000000000..27da0402f --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/Notification.java @@ -0,0 +1,75 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.*; +import lombok.Data; + +import java.util.Date; + +/** + * Persisted record of a notification that was sent (or attempted) to a user. + * Backs the "notification list with unread count" feature. + */ +@Data +@Entity +@Table(name = "notification") +public class Notification { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "app_type") + private String appType; + + @Column(name = "sender_user_id") + private Integer senderUserId; + + @Column(name = "receiver_user_id", nullable = false) + private Integer receiverUserId; + + @Column(name = "title") + private String title; + + @Column(name = "body") + private String body; + + @Column(name = "notification_type") + private String notificationType; + + @Column(name = "nav_id") + private String navId; + + @Column(name = "redirect") + private String redirect; + + @Column(name = "priority") + private String priority; + + @Column(name = "is_read", nullable = false) + private boolean isRead = false; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_date", nullable = false, updatable = false) + private Date createdDate; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "read_date") + private Date readDate; + + @PrePersist + protected void onCreate() { + this.createdDate = new Date(); + if (!isRead) { + this.readDate = null; + } + } + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "synced_date") + private Date syncedDate; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/OptionCondition.java b/src/main/java/com/iemr/flw/domain/iemr/OptionCondition.java index fbb12ce74..3a8908466 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/OptionCondition.java +++ b/src/main/java/com/iemr/flw/domain/iemr/OptionCondition.java @@ -96,6 +96,7 @@ public class OptionCondition { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/OrsDistribution.java b/src/main/java/com/iemr/flw/domain/iemr/OrsDistribution.java index e5603deef..4e633303b 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/OrsDistribution.java +++ b/src/main/java/com/iemr/flw/domain/iemr/OrsDistribution.java @@ -39,4 +39,10 @@ public class OrsDistribution { @Column(name = "updated_at", insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP") private java.sql.Timestamp updatedAt; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private java.sql.Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/OtpBeneficiary.java b/src/main/java/com/iemr/flw/domain/iemr/OtpBeneficiary.java index 15a87bd0e..be82820b8 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/OtpBeneficiary.java +++ b/src/main/java/com/iemr/flw/domain/iemr/OtpBeneficiary.java @@ -30,4 +30,11 @@ public class OtpBeneficiary { @Column(name = "createdAt", updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") private Timestamp createdAt; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } 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 4dea0ce7c..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; @@ -57,4 +59,10 @@ public class PHCReviewForm { @Column(name = "form_type") private String formType; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/PMSMA.java b/src/main/java/com/iemr/flw/domain/iemr/PMSMA.java index f1320fee9..65b5c1607 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PMSMA.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PMSMA.java @@ -141,4 +141,11 @@ public class PMSMA { @Column(name = "anyOtherHighRiskCondition") private String anyOtherHighRiskCondition; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/PNCVisit.java b/src/main/java/com/iemr/flw/domain/iemr/PNCVisit.java index 04eb6f4a8..64bd2cbc8 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PNCVisit.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PNCVisit.java @@ -94,4 +94,11 @@ public class PNCVisit { @Column(name = "delivery_discharge_summary_image4") private String deliveryDischargeSummary4; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/PhyGeneralExamination.java b/src/main/java/com/iemr/flw/domain/iemr/PhyGeneralExamination.java index b99fd804b..466845c1d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PhyGeneralExamination.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PhyGeneralExamination.java @@ -74,6 +74,7 @@ public class PhyGeneralExamination { @Column(name = "SyncedBy") private String syncedBy; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskAssess.java b/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskAssess.java index 8e61655f4..b070ea61a 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskAssess.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskAssess.java @@ -55,4 +55,11 @@ public class PregnantWomanHighRiskAssess { @Column(name = "is_high_risk") private Boolean isHighRisk; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskTrack.java b/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskTrack.java index 516cabfa1..f4c64623d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskTrack.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanHighRiskTrack.java @@ -98,4 +98,10 @@ public class PregnantWomanHighRiskTrack { @Column(name = "is_deleted") private Boolean isDeleted; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanRegister.java b/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanRegister.java index 736f3afe3..9dba97dd7 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanRegister.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PregnantWomanRegister.java @@ -116,5 +116,11 @@ public class PregnantWomanRegister { @Column(name = "updated_by") private String updatedBy; -} + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; +} diff --git a/src/main/java/com/iemr/flw/domain/iemr/PrescribedDrugDetail.java b/src/main/java/com/iemr/flw/domain/iemr/PrescribedDrugDetail.java index 9a0a1502c..bbf63f08c 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PrescribedDrugDetail.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PrescribedDrugDetail.java @@ -77,6 +77,7 @@ public class PrescribedDrugDetail { @Column(name = "SyncedBy") private String syncedBy; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/PrescriptionDetail.java b/src/main/java/com/iemr/flw/domain/iemr/PrescriptionDetail.java index 5bc5295d6..a0dd33a29 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PrescriptionDetail.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PrescriptionDetail.java @@ -65,6 +65,7 @@ public class PrescriptionDetail { @Column(name = "SyncedBy") private String syncedBy; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/PulsePolioCampaign.java b/src/main/java/com/iemr/flw/domain/iemr/PulsePolioCampaign.java index ddf7b8b60..af0bd0914 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/PulsePolioCampaign.java +++ b/src/main/java/com/iemr/flw/domain/iemr/PulsePolioCampaign.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.Data; +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; @@ -56,5 +57,10 @@ protected void onUpdate() { this.updatedDate = LocalDateTime.now(); } + @Column(name = "synced_by") + private String syncedBy; + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/QuestionOption.java b/src/main/java/com/iemr/flw/domain/iemr/QuestionOption.java index 9a35f1a82..cbbbdc83d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/QuestionOption.java +++ b/src/main/java/com/iemr/flw/domain/iemr/QuestionOption.java @@ -108,6 +108,7 @@ public class QuestionOption { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/QuestionResponse.java b/src/main/java/com/iemr/flw/domain/iemr/QuestionResponse.java index d743ad2dc..0c1821856 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/QuestionResponse.java +++ b/src/main/java/com/iemr/flw/domain/iemr/QuestionResponse.java @@ -92,6 +92,7 @@ public class QuestionResponse { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/QuestionValidation.java b/src/main/java/com/iemr/flw/domain/iemr/QuestionValidation.java index ebfe9ac33..ee2c3a34d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/QuestionValidation.java +++ b/src/main/java/com/iemr/flw/domain/iemr/QuestionValidation.java @@ -94,6 +94,7 @@ public class QuestionValidation { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/SamVisit.java b/src/main/java/com/iemr/flw/domain/iemr/SamVisit.java index 568226095..57957847d 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/SamVisit.java +++ b/src/main/java/com/iemr/flw/domain/iemr/SamVisit.java @@ -70,4 +70,11 @@ public class SamVisit { @Column(name = "updated_date") private LocalDateTime updatedDate = LocalDateTime.now(); + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private java.sql.Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/SammelanAttachment.java b/src/main/java/com/iemr/flw/domain/iemr/SammelanAttachment.java index e94dd2bc5..e98e84d10 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/SammelanAttachment.java +++ b/src/main/java/com/iemr/flw/domain/iemr/SammelanAttachment.java @@ -1,5 +1,6 @@ package com.iemr.flw.domain.iemr; +import java.sql.Timestamp; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; @@ -25,4 +26,11 @@ public class SammelanAttachment { private String fileType; @Lob private byte[] fileData; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/SammelanRecord.java b/src/main/java/com/iemr/flw/domain/iemr/SammelanRecord.java index 1ccc5bbd6..edef292e5 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/SammelanRecord.java +++ b/src/main/java/com/iemr/flw/domain/iemr/SammelanRecord.java @@ -51,4 +51,11 @@ public class SammelanRecord { @Lob @Column(columnDefinition = "LONGTEXT",name = "attachments") private String attachments; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/ScreeningAesje.java b/src/main/java/com/iemr/flw/domain/iemr/ScreeningAesje.java index 372908f99..2814c1e9e 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ScreeningAesje.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ScreeningAesje.java @@ -101,5 +101,13 @@ public class ScreeningAesje { @Column(name = "beneficiary_status_id") private Integer beneficiaryStatusId; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "synced_date") + private Date syncedDate; + // Getters and Setters } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/ScreeningFilariasis.java b/src/main/java/com/iemr/flw/domain/iemr/ScreeningFilariasis.java index e75cb046c..79bcc3992 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ScreeningFilariasis.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ScreeningFilariasis.java @@ -83,4 +83,11 @@ public class ScreeningFilariasis { @Column(name = "user_id") private Integer userId; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "synced_date") + private Date syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/ScreeningKalaAzar.java b/src/main/java/com/iemr/flw/domain/iemr/ScreeningKalaAzar.java index d6bcd7b6c..5e62152f7 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ScreeningKalaAzar.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ScreeningKalaAzar.java @@ -108,4 +108,12 @@ public class ScreeningKalaAzar { @Column(name = "beneficiary_status") private String beneficiaryStatus; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "synced_date") + private Date syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/ScreeningLeprosy.java b/src/main/java/com/iemr/flw/domain/iemr/ScreeningLeprosy.java index 6a485eef7..22adb1f17 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ScreeningLeprosy.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ScreeningLeprosy.java @@ -225,4 +225,11 @@ public class ScreeningLeprosy { @Column(name = "LastModDate") private Timestamp lastModDate; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/ScreeningMalaria.java b/src/main/java/com/iemr/flw/domain/iemr/ScreeningMalaria.java index a310acb56..9d8ae1f3f 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/ScreeningMalaria.java +++ b/src/main/java/com/iemr/flw/domain/iemr/ScreeningMalaria.java @@ -139,5 +139,11 @@ public class ScreeningMalaria { @Column(name = "visit_date") private Date visitDate; + @Column(name = "synced_by") + private String syncedBy; + @org.hibernate.annotations.CreationTimestamp + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "synced_date") + private Date syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/SectionQuestion.java b/src/main/java/com/iemr/flw/domain/iemr/SectionQuestion.java index a50418d0e..5660405ce 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/SectionQuestion.java +++ b/src/main/java/com/iemr/flw/domain/iemr/SectionQuestion.java @@ -129,6 +129,7 @@ public class SectionQuestion { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/SectionResponse.java b/src/main/java/com/iemr/flw/domain/iemr/SectionResponse.java index d8877a50c..b75ac79a6 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/SectionResponse.java +++ b/src/main/java/com/iemr/flw/domain/iemr/SectionResponse.java @@ -85,6 +85,7 @@ public class SectionResponse { @Column(name = "vanSerialNo") private Long vanSerialNo; + @org.hibernate.annotations.CreationTimestamp @Column(name = "SyncedDate") private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/domain/iemr/StopTBDiagnostics.java b/src/main/java/com/iemr/flw/domain/iemr/StopTBDiagnostics.java index f9abde570..14e4aba6a 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/StopTBDiagnostics.java +++ b/src/main/java/com/iemr/flw/domain/iemr/StopTBDiagnostics.java @@ -135,6 +135,13 @@ public class StopTBDiagnostics { @Column(name = "processed") private String processed = "N"; - @Column(name = "vanSerialNo") + @Column(name = "vanSerialNo", updatable = false) private Long vanSerialNo; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralExamination.java b/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralExamination.java index 9972d916d..6cfeaa28e 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralExamination.java +++ b/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralExamination.java @@ -130,4 +130,11 @@ public class StopTBGeneralExamination { @Column(name = "ben_id") private Long benId; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralOpd.java b/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralOpd.java index 4cfbb912a..a688b27d6 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralOpd.java +++ b/src/main/java/com/iemr/flw/domain/iemr/StopTBGeneralOpd.java @@ -78,4 +78,11 @@ public class StopTBGeneralOpd { @Column(name = "benVisitID") private Long benVisitID; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/StopTBRegistration.java b/src/main/java/com/iemr/flw/domain/iemr/StopTBRegistration.java index 4ce54a0db..551a37370 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/StopTBRegistration.java +++ b/src/main/java/com/iemr/flw/domain/iemr/StopTBRegistration.java @@ -76,4 +76,10 @@ public class StopTBRegistration { @Column(name = "deleted") private Boolean deleted; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/TBConfirmedCase.java b/src/main/java/com/iemr/flw/domain/iemr/TBConfirmedCase.java index 00ee0dd44..96111ac7b 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/TBConfirmedCase.java +++ b/src/main/java/com/iemr/flw/domain/iemr/TBConfirmedCase.java @@ -105,4 +105,11 @@ public class TBConfirmedCase { @Column(name = "side_effects") private String sideEffects; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private java.sql.Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/TBSuspected.java b/src/main/java/com/iemr/flw/domain/iemr/TBSuspected.java index 8aa015ec1..be27aaa57 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/TBSuspected.java +++ b/src/main/java/com/iemr/flw/domain/iemr/TBSuspected.java @@ -122,4 +122,11 @@ public class TBSuspected { @Column(name = "visitCode") private Long visitCode; + + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } diff --git a/src/main/java/com/iemr/flw/domain/iemr/TbReferralFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/TbReferralFollowUp.java new file mode 100644 index 000000000..206984dfc --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/TbReferralFollowUp.java @@ -0,0 +1,54 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.lang.reflect.Type; +import java.sql.Timestamp; + +@Data +@Entity +@Table(name = "tb_referral_follow_up",schema = "db_iemr") +public class TbReferralFollowUp { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "ben_id") + private Long benId; + + @Column(name = "household_id") + private Long houseHoldId; + + @Column(name = "referred_on_date") + private Timestamp referredOnDate; + + @Column(name = "follow_up_date") + private Timestamp followUpDate; + + @Column(name = "follow_up_status") + private String followUpStatus; + + @CreationTimestamp + @Column(name = "created_date") + private Timestamp createdDate; + + @UpdateTimestamp + @Column(name = "updated_date") + private Timestamp updatedDate; + + @Column(name = "created_by") + private String createdBy; + + @Column(name = "updated_by") + private String updatedBy; + + @Column(name = "synced_by") + private String syncedBy; + + @Column(name = "user_id") + private Integer userId; + +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/TbTptFollowUp.java b/src/main/java/com/iemr/flw/domain/iemr/TbTptFollowUp.java new file mode 100644 index 000000000..15900272c --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/TbTptFollowUp.java @@ -0,0 +1,82 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.sql.Timestamp; + +@Data +@Entity +@Table(name = "tb_tpt_follow_up",schema = "db_iemr") +public class TbTptFollowUp { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "ben_id") + private Long benId; + + @Column(name = "household_id") + private Long houseHoldId; + @Column(name = "regimen_type") + private String regimenType; + + @Column(name = "treatment_start_date") + private Timestamp treatmentStartDate; + + @Column(name = "expected_treatment_completion_date") + private Timestamp expectedTreatmentCompletionDate; + + @Column(name = "follow_up_date") + private Timestamp followUpDate; + + @Column(name = "follow_up_month") + private String followUpMonth; + + @Column(name = "adherence_to_medicines") + private String adherenceToMedicines; + + @Column(name = "any_discomfort") + private Boolean anyDiscomfort; + + @Column(name = "treatment_completed") + private Boolean treatmentCompleted; + + @Column(name = "actual_treatment_completion_date") + private Timestamp actualTreatmentCompletionDate; + + @Column(name = "tpt_outcome") + private String tptOutcome; + + @Column(name = "date_of_death") + private Timestamp dateOfDeath; + + @Column(name = "place_of_death") + private String placeOfDeath; + + @Column(name = "reason_for_death") + private String reasonForDeath; + + @CreationTimestamp + @Column(name = "created_date") + private Timestamp createdDate; + + @UpdateTimestamp + @Column(name = "updated_date") + private Timestamp updatedDate; + + @Column(name = "created_by") + private String createdBy; + + @Column(name = "updated_by") + private String updatedBy; + + @Column(name = "synced_by") + private String syncedBy; + + @Column(name = "user_id") + private Integer userId; + +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/UserFcmTokenData.java b/src/main/java/com/iemr/flw/domain/iemr/UserFcmTokenData.java new file mode 100644 index 000000000..7005ba1e6 --- /dev/null +++ b/src/main/java/com/iemr/flw/domain/iemr/UserFcmTokenData.java @@ -0,0 +1,30 @@ +package com.iemr.flw.domain.iemr; + +import jakarta.persistence.*; +import lombok.Data; + +import java.sql.Timestamp; + +import static jakarta.persistence.GenerationType.IDENTITY; + +@Entity +@Table(name = "user_fcm_tokens", schema = "db_iemr") +@Data +public class UserFcmTokenData { + @Id + @GeneratedValue(strategy = IDENTITY) + private int id; + @Column(name = "user_id") + private Integer userId; + @Column(name = "token") + private String token; + @Column(name = "updated_at") + private Timestamp updatedAt; + @Column(name = "synced_by") + private String syncedBy; + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; +} + + diff --git a/src/main/java/com/iemr/flw/domain/iemr/UwinSession.java b/src/main/java/com/iemr/flw/domain/iemr/UwinSession.java index 9ab6d5cb3..f87ed3177 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/UwinSession.java +++ b/src/main/java/com/iemr/flw/domain/iemr/UwinSession.java @@ -39,5 +39,12 @@ public class UwinSession { @Column(name = "created_by") private String createdBy; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + } 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 758cc449a..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; @@ -77,5 +79,11 @@ public class VHNDForm { @Column(name = "created_date", updatable = false) private Timestamp createdDate; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/domain/iemr/Vaccine.java b/src/main/java/com/iemr/flw/domain/iemr/Vaccine.java index 0a03d259e..6975d7a8f 100644 --- a/src/main/java/com/iemr/flw/domain/iemr/Vaccine.java +++ b/src/main/java/com/iemr/flw/domain/iemr/Vaccine.java @@ -70,4 +70,11 @@ public class Vaccine { @Column(name = "LastModDate") private Timestamp lastModDate; + @Column(name = "synced_by") + private String syncedBy; + + @org.hibernate.annotations.CreationTimestamp + @Column(name = "synced_date") + private Timestamp syncedDate; + } 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/PaymentResponse.java b/src/main/java/com/iemr/flw/dto/PaymentResponse.java new file mode 100644 index 000000000..c9fcd2b0e --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/PaymentResponse.java @@ -0,0 +1,15 @@ +package com.iemr.flw.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class PaymentResponse { + + @JsonProperty("submission_id") + private String submissionId; + + private String status; + + private String receipt; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/identity/GetBenRequestHandler.java b/src/main/java/com/iemr/flw/dto/identity/GetBenRequestHandler.java index 94bfed1c1..957d8a86d 100644 --- a/src/main/java/com/iemr/flw/dto/identity/GetBenRequestHandler.java +++ b/src/main/java/com/iemr/flw/dto/identity/GetBenRequestHandler.java @@ -4,6 +4,7 @@ public class GetBenRequestHandler { private Integer villageID; + private Integer approvalStatus; private Timestamp fromDate; private Timestamp toDate; private Integer pageNo; @@ -112,4 +113,11 @@ public void setToDate(Timestamp toDate) { this.toDate = toDate; } + public Integer getApprovalStatus() { + return approvalStatus; + } + + public void setApprovalStatus(Integer approvalStatus) { + this.approvalStatus = approvalStatus; + } } diff --git a/src/main/java/com/iemr/flw/dto/iemr/ANCVisitDTO.java b/src/main/java/com/iemr/flw/dto/iemr/ANCVisitDTO.java index b4cf0033a..c82009cf1 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/ANCVisitDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/ANCVisitDTO.java @@ -9,6 +9,9 @@ @Data public class ANCVisitDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/AdolescentHealthDTO.java b/src/main/java/com/iemr/flw/dto/iemr/AdolescentHealthDTO.java index b59bb8eaf..e534fd252 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/AdolescentHealthDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/AdolescentHealthDTO.java @@ -8,6 +8,9 @@ import java.util.List; @Data public class AdolescentHealthDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + @NotNull(message = "User ID cannot be null") Integer userId; @NotNull(message = "Adolescent health records cannot be null") 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/AncCounsellingCareDTO.java b/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareDTO.java index 76aff81da..3f3011d3f 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareDTO.java @@ -4,7 +4,11 @@ @Data public class AncCounsellingCareDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private String formId; + private Long id ; private Long beneficiaryId; private String visitDate; private AncCounsellingCareListDTO fields; diff --git a/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareListDTO.java b/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareListDTO.java index 981e34e2b..e21bea694 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareListDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareListDTO.java @@ -1,79 +1,96 @@ package com.iemr.flw.dto.iemr; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.gson.annotations.SerializedName; import lombok.Data; @Data public class AncCounsellingCareListDTO { + @JsonProperty("home_visit_date") @SerializedName("home_visit_date") private String homeVisitDate; + @JsonProperty("visit_number") + @SerializedName("visit_number") + private String visitNumber; + + @JsonProperty("select_all") @SerializedName("select_all") private String selectAll; - @SerializedName("swelling") private String swelling; + @JsonProperty("high_bp") @SerializedName("high_bp") private String highBp; - @SerializedName("convulsions") private String convulsions; - @SerializedName("anemia") private String anemia; + @JsonProperty("reduced_fetal_movement") @SerializedName("reduced_fetal_movement") private String reducedFetalMovement; + @JsonProperty("age_risk") @SerializedName("age_risk") private String ageRisk; + @JsonProperty("child_gap") @SerializedName("child_gap") private String childGap; + @JsonProperty("short_height") @SerializedName("short_height") private String shortHeight; + @JsonProperty("pre_preg_weight") @SerializedName("pre_preg_weight") private String prePregWeight; - @SerializedName("bleeding") private String bleeding; + @JsonProperty("miscarriage_history") @SerializedName("miscarriage_history") private String miscarriageHistory; + @JsonProperty("four_plus_delivery") @SerializedName("four_plus_delivery") private String fourPlusDelivery; + @JsonProperty("first_delivery") @SerializedName("first_delivery") private String firstDelivery; + @JsonProperty("twin_pregnancy") @SerializedName("twin_pregnancy") private String twinPregnancy; + @JsonProperty("c_section_history") @SerializedName("c_section_history") private String cSectionHistory; + @JsonProperty("pre_existing_disease") @SerializedName("pre_existing_disease") private String preExistingDisease; + @JsonProperty("fever_malaria") @SerializedName("fever_malaria") private String feverMalaria; - @SerializedName("jaundice") private String jaundice; + @JsonProperty("sickle_cell") @SerializedName("sickle_cell") private String sickleCell; + @JsonProperty("prolonged_labor") @SerializedName("prolonged_labor") private String prolongedLabor; - @SerializedName("malpresentation") private String malpresentation; + } diff --git a/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareResponseDTO.java b/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareResponseDTO.java index 2e0195582..8c8f0fb86 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareResponseDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/AncCounsellingCareResponseDTO.java @@ -8,6 +8,7 @@ public class AncCounsellingCareResponseDTO { private String formId; private Long beneficiaryId; + private Long id; private String visitDate; private Map fields; // for dynamic form fields 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..b7bdbb5a0 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/BadgeEarnedDTO.java @@ -0,0 +1,22 @@ +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; + + /** + * 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/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/dto/iemr/CdrDTO.java b/src/main/java/com/iemr/flw/dto/iemr/CdrDTO.java index e39f06412..0654b5e3f 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/CdrDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/CdrDTO.java @@ -9,6 +9,9 @@ @Data public class CdrDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/ChildRegisterDTO.java b/src/main/java/com/iemr/flw/dto/iemr/ChildRegisterDTO.java index bcc26ff3a..9633a4b2b 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/ChildRegisterDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/ChildRegisterDTO.java @@ -6,6 +6,9 @@ @Data public class ChildRegisterDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/ChildVaccinationDTO.java b/src/main/java/com/iemr/flw/dto/iemr/ChildVaccinationDTO.java index d1fb5501b..fab5bc4e7 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/ChildVaccinationDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/ChildVaccinationDTO.java @@ -8,6 +8,9 @@ @Data @JsonIgnoreProperties(ignoreUnknown = true) public class ChildVaccinationDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private long id; private Long beneficiaryRegId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/DeliveryOutcomeDTO.java b/src/main/java/com/iemr/flw/dto/iemr/DeliveryOutcomeDTO.java index 5e94697a1..e54553884 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/DeliveryOutcomeDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/DeliveryOutcomeDTO.java @@ -1,6 +1,7 @@ package com.iemr.flw.dto.iemr; import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.Column; import lombok.Data; import java.sql.Timestamp; @@ -30,4 +31,6 @@ public class DeliveryOutcomeDTO { private String createdBy; private Timestamp updatedDate; private String updatedBy; + private String syncedBy; + private Timestamp syncedDate; } 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 bf77cd658..f7d6fa2bb 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/DewormingFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/DewormingFormDTO.java @@ -4,11 +4,14 @@ @Data public class DewormingFormDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private int id = 0; private String dewormingDone; 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/DynamicFormDTO.java b/src/main/java/com/iemr/flw/dto/iemr/DynamicFormDTO.java index 9f401d390..38fe27058 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/DynamicFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/DynamicFormDTO.java @@ -40,6 +40,9 @@ @NoArgsConstructor @AllArgsConstructor public class DynamicFormDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long formId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleDTO.java b/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleDTO.java index 0ec1c876e..d2b004f40 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleDTO.java @@ -11,6 +11,9 @@ @Data public class EligibleCoupleDTO implements Serializable { + private String syncedBy; + private Timestamp syncedDate; + private Long id; diff --git a/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleTrackingDTO.java b/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleTrackingDTO.java index 415fd1b19..e3e88c1bd 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleTrackingDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/EligibleCoupleTrackingDTO.java @@ -7,6 +7,9 @@ @Data public class EligibleCoupleTrackingDTO implements Serializable { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; @@ -47,4 +50,5 @@ public class EligibleCoupleTrackingDTO implements Serializable { private String dischargeSummary1; private String dischargeSummary2; + } diff --git a/src/main/java/com/iemr/flw/dto/iemr/FilariasisCampaignDTO.java b/src/main/java/com/iemr/flw/dto/iemr/FilariasisCampaignDTO.java index 2e53ee290..fda157d0d 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/FilariasisCampaignDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/FilariasisCampaignDTO.java @@ -4,6 +4,9 @@ @Data public class FilariasisCampaignDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private String visitDate; private FilariasisCampaignListDTO fields; diff --git a/src/main/java/com/iemr/flw/dto/iemr/FormResponseDTO.java b/src/main/java/com/iemr/flw/dto/iemr/FormResponseDTO.java index 8ab1e3903..3fa5d7a1d 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/FormResponseDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/FormResponseDTO.java @@ -39,6 +39,9 @@ @AllArgsConstructor @Builder public class FormResponseDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long responseId; private Long beneficiaryId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/FormSectionDTO.java b/src/main/java/com/iemr/flw/dto/iemr/FormSectionDTO.java index 6bb107958..52cc7405d 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/FormSectionDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/FormSectionDTO.java @@ -39,6 +39,9 @@ @NoArgsConstructor @AllArgsConstructor public class FormSectionDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long sectionId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/FormVersionDTO.java b/src/main/java/com/iemr/flw/dto/iemr/FormVersionDTO.java index 742b0f7b1..b7a042e8b 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/FormVersionDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/FormVersionDTO.java @@ -32,6 +32,9 @@ @NoArgsConstructor @AllArgsConstructor public class FormVersionDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long versionId; private Integer versionNumber; diff --git a/src/main/java/com/iemr/flw/dto/iemr/HbncPart1DTO.java b/src/main/java/com/iemr/flw/dto/iemr/HbncPart1DTO.java index 05fcff99b..46891d740 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/HbncPart1DTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/HbncPart1DTO.java @@ -6,6 +6,9 @@ @Data public class HbncPart1DTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/HbncPart2DTO.java b/src/main/java/com/iemr/flw/dto/iemr/HbncPart2DTO.java index 9a1becc62..0b1a9a05a 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/HbncPart2DTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/HbncPart2DTO.java @@ -6,6 +6,9 @@ @Data public class HbncPart2DTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/HbncVisitCardDTO.java b/src/main/java/com/iemr/flw/dto/iemr/HbncVisitCardDTO.java index 6ebb7ef70..59ad402c0 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/HbncVisitCardDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/HbncVisitCardDTO.java @@ -6,6 +6,9 @@ @Data public class HbncVisitCardDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/HbncVisitDTO.java b/src/main/java/com/iemr/flw/dto/iemr/HbncVisitDTO.java index 239e21c8f..2bbc345a1 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/HbncVisitDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/HbncVisitDTO.java @@ -9,6 +9,9 @@ @Data public class HbncVisitDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + @SerializedName("id") private Integer id; @@ -93,6 +96,9 @@ public class HbncVisitDTO { @Column(name = "baby_eyes_swollen") private Boolean babyEyesSwollen; + @SerializedName("is_admitted_in_sncu") + private String is_admitted_in_sncu; + } diff --git a/src/main/java/com/iemr/flw/dto/iemr/HbycDTO.java b/src/main/java/com/iemr/flw/dto/iemr/HbycDTO.java index 172132e07..a3b814d92 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/HbycDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/HbycDTO.java @@ -21,6 +21,9 @@ @Data public class HbycDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + @SerializedName("visit_day") private String visit_day; // 3 Months, 6 Months, etc. @@ -119,4 +122,6 @@ public class HbycDTO { + + } diff --git a/src/main/java/com/iemr/flw/dto/iemr/HighRiskAssessDTO.java b/src/main/java/com/iemr/flw/dto/iemr/HighRiskAssessDTO.java index a2f32c607..5d0fed84a 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/HighRiskAssessDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/HighRiskAssessDTO.java @@ -6,6 +6,9 @@ @Data public class HighRiskAssessDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Integer userId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/IRSRoundDTO.java b/src/main/java/com/iemr/flw/dto/iemr/IRSRoundDTO.java index f2d487160..89ee050d8 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/IRSRoundDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/IRSRoundDTO.java @@ -14,6 +14,9 @@ @NoArgsConstructor @AllArgsConstructor public class IRSRoundDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; @NotNull(message = "Date cannot be null") private LocalDate date; diff --git a/src/main/java/com/iemr/flw/dto/iemr/IfaDistributionDTO.java b/src/main/java/com/iemr/flw/dto/iemr/IfaDistributionDTO.java index 321437537..d9b0004d5 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/IfaDistributionDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/IfaDistributionDTO.java @@ -5,6 +5,9 @@ @Data public class IfaDistributionDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + @SerializedName("beneficiaryId") private Long beneficiaryId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/IncentiveActivityDTO.java b/src/main/java/com/iemr/flw/dto/iemr/IncentiveActivityDTO.java index 0ebf4aac6..b7d8b4578 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/IncentiveActivityDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/IncentiveActivityDTO.java @@ -8,6 +8,9 @@ @Data public class IncentiveActivityDTO implements Serializable { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; @@ -25,6 +28,7 @@ public class IncentiveActivityDTO implements Serializable { private String group; private String groupName; + private String groupCategoryName; private String fmrCode; diff --git a/src/main/java/com/iemr/flw/dto/iemr/IncentiveRecordDTO.java b/src/main/java/com/iemr/flw/dto/iemr/IncentiveRecordDTO.java index 354e762d5..5e775d702 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/IncentiveRecordDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/IncentiveRecordDTO.java @@ -47,6 +47,8 @@ public class IncentiveRecordDTO { private String groupName; + private String groupCategoryName; + private String reason; private String otherReason; @@ -59,6 +61,9 @@ public class IncentiveRecordDTO { private String supervisorRole; + private Boolean isApproved; + private String syncedBy; + private Timestamp syncedDate; diff --git a/src/main/java/com/iemr/flw/dto/iemr/InfantRegisterDTO.java b/src/main/java/com/iemr/flw/dto/iemr/InfantRegisterDTO.java index 0be067ff8..50834c5a2 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/InfantRegisterDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/InfantRegisterDTO.java @@ -37,4 +37,8 @@ public class InfantRegisterDTO { private String deliveryDischargeSummary3; private String deliveryDischargeSummary4; private String isSNCU; + private Boolean isReferredHigherFacility; + private String syncedBy; + private Timestamp syncedDate; + } diff --git a/src/main/java/com/iemr/flw/dto/iemr/LeprosyFollowUpDTO.java b/src/main/java/com/iemr/flw/dto/iemr/LeprosyFollowUpDTO.java index aaf28a291..17686fced 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/LeprosyFollowUpDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/LeprosyFollowUpDTO.java @@ -6,6 +6,9 @@ @Data public class LeprosyFollowUpDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/MalariaFollowUpDTO.java b/src/main/java/com/iemr/flw/dto/iemr/MalariaFollowUpDTO.java index f47da8ed8..b2b8054dd 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/MalariaFollowUpDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/MalariaFollowUpDTO.java @@ -29,6 +29,9 @@ import java.util.List; @Data public class MalariaFollowUpDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Integer userId; private List malariaFollowListUp; } diff --git a/src/main/java/com/iemr/flw/dto/iemr/MdsrDTO.java b/src/main/java/com/iemr/flw/dto/iemr/MdsrDTO.java index e59047aba..136ae9877 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/MdsrDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/MdsrDTO.java @@ -6,6 +6,9 @@ @Data public class MdsrDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/MicroBirthPlanDTO.java b/src/main/java/com/iemr/flw/dto/iemr/MicroBirthPlanDTO.java index 882cee359..2a6711e2a 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/MicroBirthPlanDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/MicroBirthPlanDTO.java @@ -7,6 +7,9 @@ import java.util.List; @Data public class MicroBirthPlanDTO implements Serializable { + private String syncedBy; + private java.sql.Timestamp syncedDate; + Integer userId; List entries; diff --git a/src/main/java/com/iemr/flw/dto/iemr/NotificationDTO.java b/src/main/java/com/iemr/flw/dto/iemr/NotificationDTO.java new file mode 100644 index 000000000..cf6e48ee0 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/NotificationDTO.java @@ -0,0 +1,16 @@ +package com.iemr.flw.dto.iemr; + +import lombok.Data; + +import java.util.Map; + +@Data +public class NotificationDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + + private String title; + private String body; + private String token; + private Map data; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/NotificationListDTO.java b/src/main/java/com/iemr/flw/dto/iemr/NotificationListDTO.java new file mode 100644 index 000000000..2e94be29f --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/NotificationListDTO.java @@ -0,0 +1,24 @@ +package com.iemr.flw.dto.iemr; + +import com.iemr.flw.domain.iemr.Notification; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * Response payload for the "list notifications" endpoint. + * Bundles the page of notifications together with the receiver's + * total unread count so the client can update a badge in one call. + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NotificationListDTO { + private List notifications; + private long unreadCount; + private long totalCount; + private int page; + private int size; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/iemr/OptionConditionDTO.java b/src/main/java/com/iemr/flw/dto/iemr/OptionConditionDTO.java index d43167a78..6cd86e776 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/OptionConditionDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/OptionConditionDTO.java @@ -38,6 +38,9 @@ @NoArgsConstructor @AllArgsConstructor public class OptionConditionDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long conditionId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/OrsDistributionDTO.java b/src/main/java/com/iemr/flw/dto/iemr/OrsDistributionDTO.java index 7bdfd4fd2..0b4143b89 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/OrsDistributionDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/OrsDistributionDTO.java @@ -4,6 +4,9 @@ @Data public class OrsDistributionDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long beneficiaryId; private Long houseHoldId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/PNCVisitDTO.java b/src/main/java/com/iemr/flw/dto/iemr/PNCVisitDTO.java index 9bfc4fc74..8829cfd7d 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/PNCVisitDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/PNCVisitDTO.java @@ -6,6 +6,9 @@ @Data public class PNCVisitDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; // private Integer pncVisit; 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/PmsmaDTO.java b/src/main/java/com/iemr/flw/dto/iemr/PmsmaDTO.java index 5c0adfc93..f0b4f8f99 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/PmsmaDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/PmsmaDTO.java @@ -6,6 +6,9 @@ @Data public class PmsmaDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Long benId; private String rchNumber; diff --git a/src/main/java/com/iemr/flw/dto/iemr/PregnantWomanDTO.java b/src/main/java/com/iemr/flw/dto/iemr/PregnantWomanDTO.java index 5240f6677..816db8ed7 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/PregnantWomanDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/PregnantWomanDTO.java @@ -6,6 +6,9 @@ @Data public class PregnantWomanDTO { + private String syncedBy; + private Timestamp syncedDate; + private Long id; private Long benId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/QuestionOptionDTO.java b/src/main/java/com/iemr/flw/dto/iemr/QuestionOptionDTO.java index 4831f9579..c1d969de6 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/QuestionOptionDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/QuestionOptionDTO.java @@ -38,6 +38,9 @@ @NoArgsConstructor @AllArgsConstructor public class QuestionOptionDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long optionId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/QuestionResponseDTO.java b/src/main/java/com/iemr/flw/dto/iemr/QuestionResponseDTO.java index f3ec7b399..f4002ddfa 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/QuestionResponseDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/QuestionResponseDTO.java @@ -37,6 +37,9 @@ @AllArgsConstructor @Builder public class QuestionResponseDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long questionResponseId; private Long questionId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/QuestionValidationDTO.java b/src/main/java/com/iemr/flw/dto/iemr/QuestionValidationDTO.java index 72c144c53..594c8e6e1 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/QuestionValidationDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/QuestionValidationDTO.java @@ -36,6 +36,9 @@ @NoArgsConstructor @AllArgsConstructor public class QuestionValidationDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long validationId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/SectionQuestionDTO.java b/src/main/java/com/iemr/flw/dto/iemr/SectionQuestionDTO.java index 11a814303..720b4e266 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/SectionQuestionDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/SectionQuestionDTO.java @@ -40,6 +40,9 @@ @NoArgsConstructor @AllArgsConstructor public class SectionQuestionDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long questionId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/SectionResponseDTO.java b/src/main/java/com/iemr/flw/dto/iemr/SectionResponseDTO.java index 78c918fe8..26b27324d 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/SectionResponseDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/SectionResponseDTO.java @@ -39,6 +39,9 @@ @AllArgsConstructor @Builder public class SectionResponseDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long sectionResponseId; private Long sectionId; diff --git a/src/main/java/com/iemr/flw/dto/iemr/SendNotificationRequestDTO.java b/src/main/java/com/iemr/flw/dto/iemr/SendNotificationRequestDTO.java new file mode 100644 index 000000000..b1551ab3c --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/SendNotificationRequestDTO.java @@ -0,0 +1,18 @@ +package com.iemr.flw.dto.iemr; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SendNotificationRequestDTO { + private String appType; + private String topic; + private String title; + private String body; + private String redirect; + private String notificationType; + private Integer receiverId; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/StopTBRegistrationDto.java b/src/main/java/com/iemr/flw/dto/iemr/StopTBRegistrationDto.java index 64194ff1d..84984e2dd 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/StopTBRegistrationDto.java +++ b/src/main/java/com/iemr/flw/dto/iemr/StopTBRegistrationDto.java @@ -5,6 +5,9 @@ @Data public class StopTBRegistrationDto { + private String syncedBy; + private java.sql.Timestamp syncedDate; + // Standard beneficiary fields — forwarded to Common-API @JsonRawValue diff --git a/src/main/java/com/iemr/flw/dto/iemr/TBConfirmedRequestDTO.java b/src/main/java/com/iemr/flw/dto/iemr/TBConfirmedRequestDTO.java index d3e76a4a7..9d3dab172 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/TBConfirmedRequestDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/TBConfirmedRequestDTO.java @@ -10,4 +10,5 @@ public class TBConfirmedRequestDTO { private Long userId; private List tbConfirmedList; + } diff --git a/src/main/java/com/iemr/flw/dto/iemr/TBScreeningDTO.java b/src/main/java/com/iemr/flw/dto/iemr/TBScreeningDTO.java index 36f48dfc5..4a1936d45 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/TBScreeningDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/TBScreeningDTO.java @@ -1,34 +1,32 @@ package com.iemr.flw.dto.iemr; +import jakarta.persistence.Column; import lombok.Data; import java.sql.Timestamp; +import java.util.List; @Data public class TBScreeningDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; - private Long id; + private Long id; private Long benId; + private Long benRegId; + private Long visitCode; private Timestamp visitDate; private Boolean coughMoreThan2Weeks; - private Boolean bloodInSputum; - private Boolean feverMoreThan2Weeks; - private Boolean lossOfWeight; - private Boolean nightSweats; - private Boolean historyOfTb; - private Boolean takingAntiTBDrugs; - private Boolean familySufferingFromTB; - private Boolean riseOfFever; private Boolean lossOfAppetite; private Boolean age; @@ -37,7 +35,63 @@ public class TBScreeningDTO { private Boolean bmi; private Boolean contactWithTBPatient; private Boolean historyOfTBInLastFiveYrs; + + // Additional screening fields + private String riskFactor; + private Boolean fatigue; + private Boolean chestPain; + private Boolean shortBreath; + + // Screening option IDs + private Integer bloodCheckId; + private Integer coughCheckId; + private Integer familyCheckId; + private Integer feverCheckId; + private Integer historyCheckId; + private Integer weightCheckId; + private Integer sweatsCheckId; + private Integer drugsCheckId; + private Integer lossOfAppetiteId; + private Integer riseOfFeverId; + private String sympotomatic; private String asymptomatic; private String recommandateTest; -} + + // Recommended tests and referrals + private String recommendedForLiquidCulture; + private Integer recommendedForLiquidCultureId; + + private String recommendedForTruenat; + private Integer recommendedForTruenatId; + + private String referredForDigitalChestXray; + private Integer referredForDigitalChestXrayId; + + private String referredForSputumCollection; + private Integer referredForSputumCollectionId; + + private String sputumSampleSubmittedAt; + private String testDenialReasons; + + // Audit fields + private String createdBy; + private Timestamp createdDate; + private Boolean deleted; + private Timestamp lastModDate; + private String modifiedBy; + private String processed; + + // Service and location details + private Integer parkingPlaceID; + private Integer providerServiceMapId; + private Integer vanID; + private Long vanSerialNo; + private Integer hivStatusId; + private String hivStatus; + private List keyPopulationRiskFactorIds; + private List keyPopulationRiskFactors; + private Boolean failureToGainWeight; + private Boolean decreasedActivityOrPlayfulness ; + +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/dto/iemr/TBSuspectedDTO.java b/src/main/java/com/iemr/flw/dto/iemr/TBSuspectedDTO.java index d09fa9bf8..23e568ef7 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/TBSuspectedDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/TBSuspectedDTO.java @@ -6,6 +6,9 @@ @Data public class TBSuspectedDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private Long id; private Timestamp updateDate; diff --git a/src/main/java/com/iemr/flw/dto/iemr/TbReferralFollowUpDTO.java b/src/main/java/com/iemr/flw/dto/iemr/TbReferralFollowUpDTO.java new file mode 100644 index 000000000..a0a3bcee7 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/TbReferralFollowUpDTO.java @@ -0,0 +1,14 @@ +package com.iemr.flw.dto.iemr; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class TbReferralFollowUpDTO { + private Long benId; + private Integer userId; + private Long houseHoldId; + TbReferralFollowUpListDTO fields; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/TbReferralFollowUpListDTO.java b/src/main/java/com/iemr/flw/dto/iemr/TbReferralFollowUpListDTO.java new file mode 100644 index 000000000..a9f3e07ea --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/TbReferralFollowUpListDTO.java @@ -0,0 +1,14 @@ +package com.iemr.flw.dto.iemr; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TbReferralFollowUpListDTO { + @JsonProperty("referred_on_date") + private String referredOnDate; + @JsonProperty("follow_up_date") + private String followUpDate; + @JsonProperty("follow_up_status") + private String followUpStatus; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/TbTptFollowUpDTO.java b/src/main/java/com/iemr/flw/dto/iemr/TbTptFollowUpDTO.java new file mode 100644 index 000000000..370da8eb6 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/TbTptFollowUpDTO.java @@ -0,0 +1,15 @@ +package com.iemr.flw.dto.iemr; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TbTptFollowUpDTO { + @JsonProperty("ben_id") + private Long benId; + @JsonProperty("user_id") + private Integer userId; + @JsonProperty("house_hold_id") + private Long houseHoldId; + TbTptFollowUpListDTO fields; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/TbTptFollowUpListDTO.java b/src/main/java/com/iemr/flw/dto/iemr/TbTptFollowUpListDTO.java new file mode 100644 index 000000000..92b5cb026 --- /dev/null +++ b/src/main/java/com/iemr/flw/dto/iemr/TbTptFollowUpListDTO.java @@ -0,0 +1,46 @@ +package com.iemr.flw.dto.iemr; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TbTptFollowUpListDTO { + @JsonProperty("regimen_type") + private String regimenType; + + @JsonProperty("treatment_start_date") + private String treatmentStartDate; + + @JsonProperty("expected_treatment_completion_date") + private String expectedTreatmentCompletionDate; + + @JsonProperty("follow_up_date") + private String followUpDate; + + @JsonProperty("monthly_follow_up") + private String monthlyFollowUp; + + @JsonProperty("medicine_adherence") + private String medicineAdherence; + + @JsonProperty("any_discomfort") + private String anyDiscomfort; + + @JsonProperty("treatment_completed") + private String treatmentCompleted; + + @JsonProperty("actual_completion_date") + private String actualCompletionDate; + + @JsonProperty("tpt_outcome") + private String tptOutcome; + + @JsonProperty("date_of_death") + private String dateOfDeath; + + @JsonProperty("place_of_death") + private String placeOfDeath; + + @JsonProperty("reason_for_death") + private String reasonForDeath; +} diff --git a/src/main/java/com/iemr/flw/dto/iemr/UpdateClaimedStatusRequestDTO.java b/src/main/java/com/iemr/flw/dto/iemr/UpdateClaimedStatusRequestDTO.java index 052b7d12f..556031b64 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/UpdateClaimedStatusRequestDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/UpdateClaimedStatusRequestDTO.java @@ -8,4 +8,5 @@ public class UpdateClaimedStatusRequestDTO { private Integer month; private Integer year; private boolean isClaimed; + private String incentiveId; } 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 bee7f2475..3ab437857 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/VHNDFormDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/VHNDFormDTO.java @@ -29,11 +29,14 @@ @Data public class VHNDFormDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + 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/VaccineDTO.java b/src/main/java/com/iemr/flw/dto/iemr/VaccineDTO.java index a3019210b..5f91cdde6 100644 --- a/src/main/java/com/iemr/flw/dto/iemr/VaccineDTO.java +++ b/src/main/java/com/iemr/flw/dto/iemr/VaccineDTO.java @@ -6,6 +6,9 @@ @Data @JsonIgnoreProperties(ignoreUnknown = true) public class VaccineDTO { + private String syncedBy; + private java.sql.Timestamp syncedDate; + private short vaccineId; private String immunizationService; 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/integration/provider/emrlite/EmrLiteRestTemplateConfig.java b/src/main/java/com/iemr/flw/integration/provider/emrlite/EmrLiteRestTemplateConfig.java index 87079a183..16af36ecf 100644 --- a/src/main/java/com/iemr/flw/integration/provider/emrlite/EmrLiteRestTemplateConfig.java +++ b/src/main/java/com/iemr/flw/integration/provider/emrlite/EmrLiteRestTemplateConfig.java @@ -14,10 +14,17 @@ public class EmrLiteRestTemplateConfig { private static final int VENDOR_CALL_TIMEOUT_MS = 20000; + // Result responses carry document assets inline as base64, so reads get more headroom than connects. + private static final int VENDOR_READ_TIMEOUT_MS = 60000; + private static final int PING_TIMEOUT_MS = 5000; + // Timeouts are required, not optional: order pushes run while holding the per-beneficiary+orderType + // order lock (see DiagnosticOrderServiceImpl), so a hung vendor call without them would hold that lock forever. @Bean public RestTemplate emrLiteRestTemplate(EmrLiteTokenManager emrLiteTokenManager) { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout(VENDOR_CALL_TIMEOUT_MS); + factory.setReadTimeout(VENDOR_READ_TIMEOUT_MS); RestTemplate restTemplate = new RestTemplate(factory); restTemplate.getInterceptors().add(new EmrLiteAuthInterceptor(emrLiteTokenManager)); return restTemplate; @@ -28,8 +35,8 @@ public RestTemplate emrLiteRestTemplate(EmrLiteTokenManager emrLiteTokenManager) @Bean public RestTemplate emrLitePingRestTemplate() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); - factory.setConnectTimeout(VENDOR_CALL_TIMEOUT_MS); - factory.setReadTimeout(VENDOR_CALL_TIMEOUT_MS); + factory.setConnectTimeout(PING_TIMEOUT_MS); + factory.setReadTimeout(PING_TIMEOUT_MS); return new RestTemplate(factory); } } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/masterEnum/IncentiveApprovalStatus.java b/src/main/java/com/iemr/flw/masterEnum/IncentiveApprovalStatus.java index e40ecc980..4149cab0f 100644 --- a/src/main/java/com/iemr/flw/masterEnum/IncentiveApprovalStatus.java +++ b/src/main/java/com/iemr/flw/masterEnum/IncentiveApprovalStatus.java @@ -5,7 +5,8 @@ public enum IncentiveApprovalStatus { VERIFIED(101, "Verified"), PENDING(102, "Pending"), REJECTED(103, "Rejected"), - OVERDUE(104, "Overdue"); + OVERDUE(104, "Overdue"), + APPROVED(105, "Approved"); private final int code; private final String label; diff --git a/src/main/java/com/iemr/flw/repo/identity/BeneficiaryRepo.java b/src/main/java/com/iemr/flw/repo/identity/BeneficiaryRepo.java index a1b0b9333..498bc3720 100644 --- a/src/main/java/com/iemr/flw/repo/identity/BeneficiaryRepo.java +++ b/src/main/java/com/iemr/flw/repo/identity/BeneficiaryRepo.java @@ -107,6 +107,13 @@ Page getBenDataWithinDates( @Query(nativeQuery = true, value = " SELECT HealthID,HealthIdNumber,isNewAbha FROM db_iemr.t_healthid WHERE HealthIdNumber = :healthIdNumber ") ArrayList getBenHealthDetails(@Param("healthIdNumber") String healthIdNumber); + // Same rows FHIR-API getBenhealthID reads, so the worklist can fill healthId/healthIdNumber without an HTTP call + @Query(nativeQuery = true, value = " SELECT HealthID, HealthIdNumber FROM db_iemr.m_benhealthidmapping WHERE BeneficiaryRegID = :benRegId ") + List getHealthIdMappingRows(@Param("benRegId") Long benRegId); + + @Query(nativeQuery = true, value = " SELECT HealthId FROM db_iemr.t_healthid WHERE HealthIdNumber = :healthIdNumber ") + List getHealthIdByNumber(@Param("healthIdNumber") String healthIdNumber); + // benRegId has no unique constraint on i_beneficiarymapping — a beneficiary can have // more than one mapping row, so this must return a list, not assume a single result. @Query("SELECT b FROM RMNCHMBeneficiarymapping b WHERE b.benRegId = :benRegId ORDER BY b.benMapId DESC") @@ -169,4 +176,9 @@ SELECT COUNT(*) @Query("SELECT t FROM RMNCHMBeneficiaryaddress t WHERE t.benAddressID IN :ids") List findAddressesByIds(@Param("ids") List ids); + + @Query("SELECT b.BenRegId, b.benficieryid " + + "FROM RMNCHBeneficiaryDetailsRmnch b " + + "WHERE b.BenRegId IN :regIds AND b.benficieryid IS NOT NULL") + List getBenIdsFromRegIds(@Param("regIds") List regIds); } diff --git a/src/main/java/com/iemr/flw/repo/iemr/AshaSupervisorLoginRepo.java b/src/main/java/com/iemr/flw/repo/iemr/AshaSupervisorLoginRepo.java index 78bb368bf..13b773717 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/AshaSupervisorLoginRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/AshaSupervisorLoginRepo.java @@ -55,6 +55,150 @@ List getAshasAtFacility( @Param("endDate") Timestamp endDate); +// @Query(value = "SELECT DISTINCT asm.ashaUserID, u.FirstName, u.LastName, " +// + "COALESCE(u.AgentID,'') AS agentID, " +// + "COALESCE(u.EmergencyContactNo,'') AS mobile, " +// + "COALESCE(g.GenderName,'') AS gender " +// + "FROM asha_supervisor_mapping asm " +// + "JOIN m_User u ON u.UserID = asm.ashaUserID " +// + "AND u.Deleted = false " +// + "LEFT JOIN m_gender g ON g.GenderID = u.GenderID " +// + "JOIN incentive_activity_record iar " +// + "ON iar.asha_id = asm.ashaUserID " +// + "WHERE asm.supervisorUserID = :supervisorUserID " +// + "AND asm.facilityID = :facilityID " +// + "AND asm.deleted = false " +// + "AND iar.is_claimed = true " +// + "AND iar.start_date >= :startDate " +// + "AND iar.end_date < :endDate " +// + "AND (" +// + " :approvalStatus = 0 " +// + " OR (:approvalStatus = 105 AND iar.approval_status IN (101,105)) " +// + " OR (:approvalStatus = 106 AND iar.approval_status = 102) " +// + " OR (:approvalStatus NOT IN (105,106) AND iar.approval_status = :approvalStatus)" +// + ")", +// nativeQuery = true) +// List getAshasAtFacilityCg( +// @Param("supervisorUserID") Integer supervisorUserID, +// @Param("facilityID") Integer facilityID, +// @Param("approvalStatus") Integer approvalStatus, +// @Param("startDate") Timestamp startDate, +// @Param("endDate") Timestamp endDate); + + + @Query(value = + "SELECT DISTINCT " + + "asm.ashaUserID, " + + "u.FirstName, " + + "u.LastName, " + + "COALESCE(u.AgentID, '') AS agentID, " + + "COALESCE(u.EmergencyContactNo, '') AS mobile, " + + "COALESCE(g.GenderName, '') AS gender " + + + "FROM asha_supervisor_mapping asm " + + + "JOIN m_User u " + + "ON u.UserID = asm.ashaUserID " + + "AND u.Deleted = false " + + + "LEFT JOIN m_gender g " + + "ON g.GenderID = u.GenderID " + + + "JOIN incentive_activity_record iar " + + "ON iar.asha_id = asm.ashaUserID " + + + "WHERE asm.supervisorUserID = :supervisorUserID " + + "AND asm.facilityID = :facilityID " + + "AND asm.deleted = false " + + "AND iar.is_default_activity = true " + + "AND iar.start_date >= :startDate " + + "AND iar.end_date < :endDate " + + + "AND (" + + " (:approvalStatus = 0) " + + + " OR (:approvalStatus = 104 " + + " AND iar.is_claimed = true " + + " AND iar.approval_status IN (102, 105)) " + + + " OR (:approvalStatus = 105 " + + " AND iar.is_claimed = true " + + " AND iar.approval_status IN (101, 105)) " + + + " OR (:approvalStatus = 106 " + + " AND iar.is_claimed = false " + + " AND iar.approval_status = 102) " + + + " OR (:approvalStatus NOT IN (104, 105, 106) " + + " AND iar.is_claimed = true " + + " AND iar.approval_status = :approvalStatus) " + + ")", + nativeQuery = true) + List getAshasAtFacilityCg( + @Param("supervisorUserID") Integer supervisorUserID, + @Param("facilityID") Integer facilityID, + @Param("approvalStatus") Integer approvalStatus, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate + ); + + @Query(value = + "SELECT DISTINCT " + + "asm.ashaUserID, " + + "u.FirstName, " + + "u.LastName, " + + "COALESCE(u.AgentID, '') AS agentID, " + + "COALESCE(u.EmergencyContactNo, '') AS mobile, " + + "COALESCE(g.GenderName, '') AS gender " + + + "FROM asha_supervisor_mapping asm " + + + "JOIN m_User u " + + "ON u.UserID = asm.ashaUserID " + + "AND u.Deleted = false " + + + "LEFT JOIN m_gender g " + + "ON g.GenderID = u.GenderID " + + + "JOIN incentive_activity_record iar " + + "ON iar.asha_id = asm.ashaUserID " + + + "WHERE asm.supervisorUserID = :supervisorUserID " + + "AND asm.deleted = false " + + "AND iar.is_default_activity = true " + + "AND iar.start_date >= :startDate " + + "AND iar.end_date < :endDate " + + + "AND (" + + " (:approvalStatus = 0) " + + + // Overdue: database status 102 or 105 + " OR (:approvalStatus = 104 " + + " AND iar.is_claimed = true " + + " AND iar.approval_status IN (102, 105)) " + + + " OR (:approvalStatus = 105 " + + " AND iar.is_claimed = true " + + " AND iar.approval_status IN (101, 105)) " + + + " OR (:approvalStatus = 106 " + + " AND iar.is_claimed = false " + + " AND iar.approval_status = 102) " + + + " OR (:approvalStatus NOT IN (104, 105, 106) " + + " AND iar.is_claimed = true " + + " AND iar.approval_status = :approvalStatus) " + + ")", + nativeQuery = true) + List getAshasAtFacilityCg( + @Param("supervisorUserID") Integer supervisorUserID, + @Param("approvalStatus") Integer approvalStatus, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate + ); + + + @Query(value = "SELECT DISTINCT asm.ashaUserID, " + "u.FirstName, " + "u.LastName, " 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..6452e6225 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/BadgeEarnedRepo.java @@ -0,0 +1,15 @@ +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 existsByUserIdAndBadgeIdAndLevelAndAwardKey( + Integer userId, String badgeId, Integer level, String awardKey); +} 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/repo/iemr/BenReferDetailsRepo.java b/src/main/java/com/iemr/flw/repo/iemr/BenReferDetailsRepo.java index 9cb356626..25a0b652a 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/BenReferDetailsRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/BenReferDetailsRepo.java @@ -51,8 +51,8 @@ public interface BenReferDetailsRepo extends JpaRepository { - @Query(value = "SELECT cv FROM ChildVaccination cv WHERE cv.createdBy = :userId and cv.createdDate >= :fromDate and cv.createdDate <= :toDate") - List getChildVaccinationDetails(@Param("userId") String userId, - @Param("fromDate") Timestamp fromDate, @Param("toDate") Timestamp toDate); + @Query(value = "SELECT cv FROM ChildVaccination cv WHERE cv.createdBy = :userId") + List getChildVaccinationDetails(@Param("userId") String userId); ChildVaccination findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName(Long benRegId, Timestamp createdDate, String vaccine); diff --git a/src/main/java/com/iemr/flw/repo/iemr/DiagnosticOrderRepo.java b/src/main/java/com/iemr/flw/repo/iemr/DiagnosticOrderRepo.java index fa3e79b48..36ecc6420 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/DiagnosticOrderRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/DiagnosticOrderRepo.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; +import java.sql.Timestamp; import java.util.List; import java.util.Optional; @@ -37,14 +38,24 @@ Optional findFirstByBeneficiaryIdAndOrderTypeAndDeletedFalseOrd "ORDER BY o.lastPolledAt ASC NULLS FIRST") List findTrueNatDueForPoll(); - @Query("SELECT o FROM DiagnosticOrder o WHERE o.status IN ('PENDING', 'IN_PROGRESS') AND o.deleted = false") - List findAllOutstandingOrders(); + // The (disabled) daily close only touches orders created within [dayStart, dayEnd) — i.e. today's. + @Query("SELECT o FROM DiagnosticOrder o WHERE o.status IN ('PENDING', 'IN_PROGRESS') AND o.deleted = false " + + "AND o.createdDate >= :dayStart AND o.createdDate < :dayEnd") + List findAllOutstandingOrders(@Param("dayStart") Timestamp dayStart, @Param("dayEnd") Timestamp dayEnd); @Transactional @Modifying @Query("UPDATE DiagnosticOrder o SET o.vanSerialNo = o.id WHERE o.id = :id") void updateVanSerialNo(@Param("id") Long id); + // A poll that changed nothing only records when it ran (and the retry count), leaving processed, + // modified_by and the rest of the row alone: no re-upload to central, no change-log line. + @Transactional + @Modifying + @Query("UPDATE DiagnosticOrder o SET o.lastPolledAt = :polledAt, o.retryCount = :retryCount WHERE o.id = :id") + int recordUnchangedPoll(@Param("id") Long id, @Param("polledAt") Timestamp polledAt, + @Param("retryCount") Integer retryCount); + // Beneficiary-status-summary queries, filtered the same way as // FormResponseRepo.findBeneficiaryIdsByFormIdAndStatusFiltered - null-safe optional // villageId/providerServiceMapId via a correlated BenFlowStatus subquery. @@ -100,4 +111,16 @@ List findBeneficiaryIdsClosed(@Param("orderType") String orderType, "AND (:providerServiceMapId IS NULL OR b.providerServiceMapId = :providerServiceMapId))") List findBeneficiaryIdsAwaitingManualEntry(@Param("orderType") String orderType, @Param("villageId") Integer villageId, @Param("providerServiceMapId") Integer providerServiceMapId); + + // The five queries above in one pass: beneficiaryId + status of each beneficiary's latest order, + // same filters, for the caller to bucket by status. + @Query("SELECT o.beneficiaryId, o.status FROM DiagnosticOrder o WHERE o.orderType = :orderType AND o.deleted = false " + + "AND o.status IN ('PENDING', 'IN_PROGRESS', 'COMPLETED', 'FAILED', 'CLOSED', 'CANCELLED', 'EXPIRED', 'REFUSED', 'MANUAL_ENTRY') " + + "AND o.id = (SELECT MAX(o2.id) FROM DiagnosticOrder o2 " + + "WHERE o2.beneficiaryId = o.beneficiaryId AND o2.orderType = :orderType AND o2.deleted = false) " + + "AND o.beneficiaryId IN (SELECT b.beneficiaryID FROM BenFlowStatus b WHERE b.deleted = false " + + "AND (:villageId IS NULL OR b.villageID = :villageId) " + + "AND (:providerServiceMapId IS NULL OR b.providerServiceMapId = :providerServiceMapId))") + List findLatestOrderStatusByBeneficiary(@Param("orderType") String orderType, + @Param("villageId") Integer villageId, @Param("providerServiceMapId") Integer providerServiceMapId); } 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 8c21f0e9f..74acfa5f8 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/IncentiveRecordRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/IncentiveRecordRepo.java @@ -71,7 +71,8 @@ IncentiveActivityRecord findRecordByActivityIdCreatedDateBenId( @Query("SELECT " + "SUM(CASE WHEN record.approvalStatus = 101 THEN 1 ELSE 0 END), " + "SUM(CASE WHEN record.approvalStatus = 102 THEN 1 ELSE 0 END), " + - "SUM(CASE WHEN record.approvalStatus = 103 THEN 1 ELSE 0 END) " + + "SUM(CASE WHEN record.approvalStatus = 103 THEN 1 ELSE 0 END), " + + "SUM(CASE WHEN record.approvalStatus = 105 THEN 1 ELSE 0 END) " + "FROM IncentiveActivityRecord record " + "WHERE record.ashaId = :ashaId " + "AND record.isClaimed = true " + @@ -83,6 +84,58 @@ List getStatusCountByAshaId( @Param("endDate") Timestamp endDate); + @Query("SELECT " + + "SUM(CASE WHEN record.approvalStatus = 101 THEN 1 ELSE 0 END), " + + "SUM(CASE WHEN record.approvalStatus = 102 THEN 1 ELSE 0 END), " + + "SUM(CASE WHEN record.approvalStatus = 103 THEN 1 ELSE 0 END), " + + "SUM(CASE WHEN record.approvalStatus IN (102,105) THEN 1 ELSE 0 END) " + + "FROM IncentiveActivityRecord record " + + "WHERE record.ashaId = :ashaId " + + "AND record.isClaimed = true " + + "AND record.createdDate >= :startDate " + + "AND record.createdDate < :endDate") + List getStatusCountByAshaIdANM( + @Param("ashaId") Integer ashaId, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate); + + @Query(""" + SELECT + SUM(CASE WHEN record.approvalStatus = 101 THEN 1 ELSE 0 END), + SUM(CASE WHEN record.approvalStatus = 102 THEN 1 ELSE 0 END), + SUM(CASE WHEN record.approvalStatus = 103 THEN 1 ELSE 0 END), + SUM(CASE WHEN record.approvalStatus IN (101,105) THEN 1 ELSE 0 END) + FROM IncentiveActivityRecord record + WHERE record.ashaId = :ashaId + AND record.isClaimed = true + AND record.isDefaultActivity = true + AND record.createdDate >= :startDate + AND record.createdDate < :endDate + """) + List getStatusCountByAshaIdOfDefaultActivity( + @Param("ashaId") Integer ashaId, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate); + + + @Query(""" + SELECT + SUM(CASE WHEN record.approvalStatus = 101 THEN 1 ELSE 0 END), + SUM(CASE WHEN record.approvalStatus = 102 THEN 1 ELSE 0 END), + SUM(CASE WHEN record.approvalStatus = 103 THEN 1 ELSE 0 END), + SUM(CASE WHEN record.approvalStatus IN (101,105) THEN 1 ELSE 0 END) + FROM IncentiveActivityRecord record + WHERE record.ashaId = :ashaId + AND record.isClaimed = false + AND record.createdDate >= :startDate + AND record.createdDate < :endDate + """) + List getStatusUnclaimedCountByAshaId( + @Param("ashaId") Integer ashaId, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate); + + @Query("SELECT COALESCE(SUM(record.amount), 0) " + "FROM IncentiveActivityRecord record " + "JOIN IncentiveActivity master ON record.activityId = master.id " + @@ -100,6 +153,99 @@ Long getTotalAmountByAsha( @Param("stateCode") Integer stateCode); + @Query(""" + SELECT COALESCE(SUM(record.amount), 0) + FROM IncentiveActivityRecord record + JOIN IncentiveActivity master ON record.activityId = master.id + WHERE record.ashaId = :ashaId + AND master.state = :stateCode + AND record.isClaimed = true + AND record.startDate >= :fromDate + AND record.endDate <= :toDate + AND ( + :approvalStatus = 0 + OR ( + :approvalStatus = 102 + AND record.approvalStatus IN (102, 105) + ) + OR ( + :approvalStatus <> 102 + AND record.approvalStatus = :approvalStatus + ) + ) + """) + Long getTotalAmountByAshaANM( + @Param("ashaId") Integer ashaId, + @Param("fromDate") Timestamp fromDate, + @Param("toDate") Timestamp toDate, + @Param("approvalStatus") Integer approvalStatus, + @Param("stateCode") Integer stateCode + ); + + @Query(""" + SELECT COALESCE(SUM(record.amount), 0) + FROM IncentiveActivityRecord record + JOIN IncentiveActivity master ON record.activityId = master.id + WHERE record.ashaId = :ashaId + AND master.state = :stateCode + AND record.isClaimed = true + AND record.isDefaultActivity = false + AND record.startDate >= :fromDate + AND record.endDate <= :toDate + AND ( + :approvalStatus = 0 + OR ( + :approvalStatus = 102 + AND record.approvalStatus IN (102, 105) + ) + OR ( + :approvalStatus <> 102 + AND record.approvalStatus = :approvalStatus + ) + ) + """) + Long getTotalAmountByAshaANMbeforeClaimedHour( + @Param("ashaId") Integer ashaId, + @Param("fromDate") Timestamp fromDate, + @Param("toDate") Timestamp toDate, + @Param("approvalStatus") Integer approvalStatus, + @Param("stateCode") Integer stateCode + ); + + @Query("SELECT COALESCE(SUM(record.amount), 0) " + + "FROM IncentiveActivityRecord record " + + "JOIN IncentiveActivity master ON record.activityId = master.id " + + "WHERE record.ashaId = :ashaId " + + "AND master.state = :stateCode " + + "AND record.isClaimed = true " + + "AND record.isDefaultActivity = true " + + "AND record.startDate >= :fromDate " + + "AND record.endDate <= :toDate " + + "AND (" + + " :approvalStatus = 0 " + + " OR (:approvalStatus = 101 AND record.approvalStatus IN (101,105)) " + + " OR (:approvalStatus <> 101 AND record.approvalStatus = :approvalStatus)" + + ")") + Long getDefaultActivityTotalAmountByAsha( + @Param("ashaId") Integer ashaId, + @Param("fromDate") Timestamp fromDate, + @Param("toDate") Timestamp toDate, + @Param("approvalStatus") Integer approvalStatus, + @Param("stateCode") Integer stateCode); + + + @Query("SELECT record " + + "FROM IncentiveActivityRecord record " + + "WHERE record.ashaId = :ashaId " + + "AND record.isClaimed = true " + + "AND record.startDate <= :toDate " + + "AND record.isDefaultActivity = true " + + "AND record.endDate >= :fromDate") + List getDefalutRecordsByAsha( + @Param("ashaId") Integer ashaId, + @Param("fromDate") Timestamp fromDate, + @Param("toDate") Timestamp toDate); + @Query("SELECT record " + "FROM IncentiveActivityRecord record " + "WHERE record.ashaId = :ashaId " + @@ -133,6 +279,148 @@ int updateApprovalStatusByAshaAndDateRange( @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, @Param("ashaSupervisorUserName") String ashaSupervisorUserName); + @Modifying + @Transactional + @Query("UPDATE IncentiveActivityRecord iar " + + "SET iar.approvalStatus = :approvalStatus, " + + "iar.verifiedByUserId = :ashaSupervisorUserId, " + + "iar.reason = NULL, " + + "iar.otherReason = NULL, " + + "iar.approvalDate = :approvalDate, " + + "iar.verifiedByUserName = :ashaSupervisorUserName " + + "WHERE iar.id = :incentiveId " + + "AND iar.ashaId = :ashaId " + + "AND iar.isClaimed = true") + int updateApprovalStatusByIncentiveId( + @Param("incentiveId") Long incentiveId, + @Param("ashaId") Integer ashaId, + @Param("approvalStatus") Integer approvalStatus, + @Param("approvalDate") Timestamp approvalDate, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName); + + + @Modifying + @Transactional + @Query(""" + UPDATE IncentiveActivityRecord iar + SET iar.approvalStatus = :approvalStatus, + iar.verifiedByUserId = :ashaSupervisorUserId, + iar.reason = NULL, + iar.otherReason = NULL, + iar.approvalDate = :approvalDate, + iar.verifiedByUserName = :ashaSupervisorUserName + WHERE iar.id IN :incentiveIds + AND iar.ashaId = :ashaId + AND iar.isClaimed = true + """) + int updateApprovalStatusByIncentiveIds( + @Param("incentiveIds") List incentiveIds, + @Param("ashaId") Integer ashaId, + @Param("approvalStatus") Integer approvalStatus, + @Param("approvalDate") Timestamp approvalDate, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName + ); + + + @Modifying + @Transactional + @Query(""" + UPDATE IncentiveActivityRecord iar + SET + iar.approvalStatus = :approvalStatus, + iar.verifiedByUserId = :ashaSupervisorUserId, + iar.verifiedByUserName = :ashaSupervisorUserName, + iar.approvalDate = :approvalDate, + iar.reason = NULL, + iar.otherReason = NULL + WHERE iar.ashaId = :ashaId + AND iar.isClaimed = true + AND iar.createdDate >= :startDate + AND iar.createdDate < :endDate + """) + int updateApprovalStatusByAshaAndDateRangeForDefaultActivity( + @Param("ashaId") Integer ashaId, + @Param("approvalStatus") Integer approvalStatus, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate, + @Param("approvalDate") Timestamp approvalDate, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName + ); + + @Modifying + @Transactional + @Query(""" + UPDATE IncentiveActivityRecord iar + SET + iar.approvalStatus = :approvalStatus, + iar.verifiedByUserId = :ashaSupervisorUserId, + iar.verifiedByUserName = :ashaSupervisorUserName, + iar.approvalDate = :approvalDate, + iar.reason = NULL, + iar.otherReason = NULL + WHERE iar.id = :incentiveId + AND iar.ashaId = :ashaId + AND iar.isClaimed = true + """) + int updateApprovalStatusByIncentiveIdForDefaultActivity( + @Param("incentiveId") Long incentiveId, + @Param("ashaId") Integer ashaId, + @Param("approvalStatus") Integer approvalStatus, + @Param("approvalDate") Timestamp approvalDate, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName + ); + + @Modifying + @Transactional + @Query(""" + UPDATE IncentiveActivityRecord iar + SET iar.approvalStatus = :approvalStatus, + iar.verifiedByUserId = :ashaSupervisorUserId, + iar.verifiedByUserName = :ashaSupervisorUserName, + iar.approvalDate = :approvalDate, + iar.reason = NULL, + iar.otherReason = NULL + WHERE iar.id IN :incentiveIds + AND iar.ashaId = :ashaId + AND iar.isClaimed = true + AND iar.isDefaultActivity = true + """) + int updateApprovalStatusByIncentiveIdsForDefaultActivity( + @Param("incentiveIds") List incentiveIds, + @Param("ashaId") Integer ashaId, + @Param("approvalStatus") Integer approvalStatus, + @Param("approvalDate") Timestamp approvalDate, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName + ); + + + @Modifying + @Transactional + @Query(""" + UPDATE IncentiveActivityRecord iar + SET iar.approvalStatus = :approvalStatus, + iar.verifiedByUserId = :ashaSupervisorUserId, + iar.verifiedByUserName = :ashaSupervisorUserName, + iar.approvalDate = :approvalDate, + iar.reason = NULL, + iar.otherReason = NULL + WHERE iar.id IN :incentiveIds + AND iar.ashaId = :ashaId + AND iar.isClaimed = true + """) + int updateApprovalStatusByIncentiveIdsForDefaultActivityForAnm( + @Param("incentiveIds") List incentiveIds, + @Param("ashaId") Integer ashaId, + @Param("approvalStatus") Integer approvalStatus, + @Param("approvalDate") Timestamp approvalDate, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName + ); @Modifying @Transactional @@ -143,9 +431,11 @@ int updateApprovalStatusByAshaAndDateRange( + "iar.otherReason = :otherReason, " + "iar.approvalDate = :approvalDate, " + "iar.verifiedByUserName = :ashaSupervisorUserName " - + "WHERE iar.isClaimed = true") + + "WHERE iar.isClaimed = true " + + "AND iar.ashaId = :ashaId") int updateApprovalStatusById( @Param("status") Integer status, + @Param("ashaId") Integer ashaId, @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, @Param("ashaSupervisorUserName") String ashaSupervisorUserName, @Param("reason") String reason, @@ -153,6 +443,55 @@ int updateApprovalStatusById( @Param("otherReason") String otherReason ); + @Modifying + @Transactional + @Query("UPDATE IncentiveActivityRecord iar " + + "SET iar.approvalStatus = :status, " + + "iar.verifiedByUserId = :ashaSupervisorUserId, " + + "iar.reason = :reason, " + + "iar.otherReason = :otherReason, " + + "iar.approvalDate = :approvalDate, " + + "iar.verifiedByUserName = :ashaSupervisorUserName " + + "WHERE iar.isClaimed = true " + + "AND iar.id = :incentiveId " + + "AND iar.ashaId = :ashaId") + int updateApprovalStatusByIdAndIncentiveId( + @Param("status") Integer status, + @Param("ashaId") Integer ashaId, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName, + @Param("reason") String reason, + @Param("approvalDate") Timestamp approvalDate, + @Param("otherReason") String otherReason, + @Param("incentiveId") Long incentiveId + ); + + + @Modifying + @Transactional + @Query(""" + UPDATE IncentiveActivityRecord iar + SET iar.approvalStatus = :status, + iar.verifiedByUserId = :ashaSupervisorUserId, + iar.verifiedByUserName = :ashaSupervisorUserName, + iar.reason = :reason, + iar.otherReason = :otherReason, + iar.approvalDate = :approvalDate + WHERE iar.id IN :incentiveIds + AND iar.ashaId = :ashaId + AND iar.isClaimed = true + """) + int updateApprovalStatusByIdsAndIncentiveIds( + @Param("status") Integer status, + @Param("ashaId") Integer ashaId, + @Param("ashaSupervisorUserId") Integer ashaSupervisorUserId, + @Param("ashaSupervisorUserName") String ashaSupervisorUserName, + @Param("reason") String reason, + @Param("approvalDate") Timestamp approvalDate, + @Param("otherReason") String otherReason, + @Param("incentiveIds") List incentiveIds + ); + @Modifying @Transactional @Query("UPDATE IncentiveActivityRecord iar " @@ -211,4 +550,52 @@ List getRecordsBySuperVisor( @Param("startDate") Timestamp startDate, @Param("endDate") Timestamp endDate, @Param("approvalStatus") Integer approvalStatus); + + @Modifying + @Transactional + @Query("UPDATE IncentiveActivityRecord iar " + + "SET iar.isClaimed = :isClaimed, " + + "iar.calimedDate = :calimedDate, " + + "iar.approvalStatus = 102 " + + "WHERE iar.ashaId = :ashaId " + + "AND iar.id = :incentiveId ") + int updateClaimStatusByAshaAndIncentiveIds( + @Param("ashaId") Integer ashaId, + @Param("isClaimed") Boolean isClaimed, + @Param("calimedDate") Timestamp calimedDate, + @Param("incentiveId") Long incentiveId + ); + + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Transactional + @Query(""" + UPDATE IncentiveActivityRecord iar + SET iar.approvalStatus = 102, + iar.isClaimed = false, + iar.calimedDate = NULL, + iar.approvalDate = NULL, + iar.reason = NULL, + iar.otherReason = NULL, + iar.verifiedByUserId = NULL, + iar.verifiedByUserName = NULL, + iar.updatedDate = :updatedDate, + iar.updatedBy = :updatedBy + WHERE iar.approvalStatus = 103 + AND iar.isClaimed = true + AND iar.endDate < :currentMonthStart + """) + int resetRejectedIncentivesForNewMonth( + @Param("currentMonthStart") Timestamp currentMonthStart, + @Param("updatedDate") Timestamp updatedDate, + @Param("updatedBy") String updatedBy + ); + + @Query("SELECT r FROM IncentiveActivityRecord r " + + "WHERE r.ashaId = :ashaId " + + "AND r.approvalStatus = :status") + List findApprovedForMonth( + @Param("ashaId") Integer ashaId, + @Param("status") Integer status); } + + diff --git a/src/main/java/com/iemr/flw/repo/iemr/NotificationRepository.java b/src/main/java/com/iemr/flw/repo/iemr/NotificationRepository.java new file mode 100644 index 000000000..c269c97c7 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/NotificationRepository.java @@ -0,0 +1,35 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.Notification; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +public interface NotificationRepository extends JpaRepository { + + /** Paged list for a receiver, newest first. */ + Page findByReceiverUserIdOrderByCreatedDateDesc(Integer receiverUserId, Pageable pageable); + + /** Cheap count for badge display. */ + long countByReceiverUserIdAndIsReadFalse(Integer receiverUserId); + + /** Mark a single notification as read. */ + @Modifying + @Transactional + @Query("UPDATE Notification n SET n.isRead = true, n.readDate = :readDate " + + "WHERE n.id = :id") + int markAsRead(@Param("id") Long id, @Param("readDate") Date readDate); + + /** Mark every unread notification for a user as read. */ + @Modifying + @Transactional + @Query("UPDATE Notification n SET n.isRead = true, n.readDate = :readDate " + + "WHERE n.receiverUserId = :receiverUserId AND n.isRead = false") + int markAllAsRead(@Param("receiverUserId") Integer receiverUserId, @Param("readDate") Date readDate); +} \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/repo/iemr/StopTBDiagnosticsRepo.java b/src/main/java/com/iemr/flw/repo/iemr/StopTBDiagnosticsRepo.java index de3fa9096..19bb9acfa 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/StopTBDiagnosticsRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/StopTBDiagnosticsRepo.java @@ -4,6 +4,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryHints; +import jakarta.persistence.QueryHint; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -19,6 +21,8 @@ public interface StopTBDiagnosticsRepo extends JpaRepository findAllByProviderServiceMapID(@Param("psmId") Integer psmId); + // Read-only: getAll only reads these rows, so Hibernate keeps no change-tracking snapshot per row + @QueryHints(@QueryHint(name = "org.hibernate.readOnly", value = "true")) @Query("SELECT d FROM StopTBDiagnostics d WHERE d.benRegID IN " + "(SELECT b.beneficiaryRegID FROM BenFlowStatus b WHERE b.providerServiceMapId = :psmId AND b.villageID = :villageId) " + "AND d.deleted = false ORDER BY d.visitDate DESC") diff --git a/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralExaminationRepo.java b/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralExaminationRepo.java index 944e99a65..798fcaf85 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralExaminationRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralExaminationRepo.java @@ -5,6 +5,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryHints; +import jakarta.persistence.QueryHint; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -32,6 +34,8 @@ StopTBGeneralExamination findByBeneficiaryRegIDAndVisitCode(@Param("beneficiaryR @Query("SELECT e FROM StopTBGeneralExamination e WHERE e.providerServiceMapID = :psmId AND e.deleted = false ORDER BY e.createdDate DESC") List findAllByProviderServiceMapID(@Param("psmId") Integer psmId); + // Read-only: getAll only reads these rows, so Hibernate keeps no change-tracking snapshot per row + @QueryHints(@QueryHint(name = "org.hibernate.readOnly", value = "true")) @Query("SELECT e FROM StopTBGeneralExamination e WHERE e.beneficiaryRegID IN " + "(SELECT b.beneficiaryRegID FROM BenFlowStatus b WHERE b.providerServiceMapId = :psmId AND b.villageID = :villageId) " + "AND e.deleted = false ORDER BY e.createdDate DESC") diff --git a/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralOpdRepo.java b/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralOpdRepo.java index 3cb8868f8..d35925a26 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralOpdRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/StopTBGeneralOpdRepo.java @@ -5,6 +5,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryHints; +import jakarta.persistence.QueryHint; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -30,6 +32,8 @@ public interface StopTBGeneralOpdRepo extends JpaRepository findAllByProviderServiceMapID(@Param("psmId") Integer psmId); + // Read-only: getAll only reads these rows, so Hibernate keeps no change-tracking snapshot per row + @QueryHints(@QueryHint(name = "org.hibernate.readOnly", value = "true")) @Query("SELECT o FROM StopTBGeneralOpd o WHERE o.benRegID IN " + "(SELECT b.beneficiaryRegID FROM BenFlowStatus b WHERE b.providerServiceMapId = :psmId AND b.villageID = :villageId) " + "AND o.deleted = false ORDER BY o.createdDate DESC") diff --git a/src/main/java/com/iemr/flw/repo/iemr/SupervisorDashboardRepo.java b/src/main/java/com/iemr/flw/repo/iemr/SupervisorDashboardRepo.java index a56587776..c947d1449 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/SupervisorDashboardRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/SupervisorDashboardRepo.java @@ -20,35 +20,96 @@ public interface SupervisorDashboardRepo extends JpaRepository getAshaIdsBySupervisor(@Param("supervisorUserID") Integer supervisorUserID); + @Query(value = "SELECT DISTINCT usrm.FacilityID " + + "FROM m_UserServiceRoleMapping usrm " + + "WHERE usrm.UserID = :userID AND usrm.Deleted = false " + + "AND usrm.FacilityID IS NOT NULL", nativeQuery = true) + List getUserFacilityIDs(@Param("userID") Integer userID); + // Get ASHAs with facility info for a supervisor - @Query(value = "SELECT DISTINCT asm.ashaUserID, u.FirstName, u.LastName, " + @Query(value = "SELECT DISTINCT u.UserID, u.FirstName, u.LastName, " + + "COALESCE(u.EmployeeID,'') AS employeeID, " + "f.FacilityID, f.FacilityName, " + "COALESCE(ft.FacilityTypeName,'') AS facilityTypeName, " - + "COALESCE(u.AgentID,'') AS agentID, " - + "COALESCE(u.EmergencyContactNo,'') AS mobile, " - + "COALESCE(g.GenderName,'') AS gender " + + "COALESCE(u.ContactNo,'') AS mobile " + "FROM asha_supervisor_mapping asm " - + "JOIN m_User u ON u.UserID = asm.ashaUserID AND u.Deleted = false " - + "JOIN m_facility f ON f.FacilityID = asm.facilityID AND f.Deleted = false " + + "JOIN m_User u ON u.UserID = asm.ashaUserID " + + "JOIN m_facility f ON f.FacilityID = asm.facilityID " + "LEFT JOIN m_facilitytype ft ON ft.FacilityTypeID = f.FacilityTypeID " - + "LEFT JOIN m_gender g ON g.GenderID = u.GenderID " + "WHERE asm.supervisorUserID = :supervisorUserID " - + "AND asm.deleted = false", nativeQuery = true) + + "AND asm.deleted = false AND u.Deleted = false AND f.Deleted = false", nativeQuery = true) List getAshasWithFacilityInfo(@Param("supervisorUserID") Integer supervisorUserID); + + @Query(value = "SELECT DISTINCT u.UserID, u.FirstName, u.LastName, " + + "COALESCE(u.EmployeeID,'') AS employeeID, " + + "f.FacilityID, f.FacilityName, " + + "COALESCE(ft.FacilityTypeName,'') AS facilityTypeName, " + + "COALESCE(u.ContactNo,'') AS mobile " + + "FROM m_UserServiceRoleMapping usrm " + + "JOIN m_User u ON u.UserID = usrm.UserID " + + "JOIN m_Role r ON r.RoleID = usrm.RoleID " + + "JOIN m_facility f ON f.FacilityID = usrm.FacilityID " + + "LEFT JOIN m_facilitytype ft ON ft.FacilityTypeID = f.FacilityTypeID " + + "WHERE usrm.FacilityID IN :facilityIDs " + + "AND r.RoleName = 'ASHA' " + + "AND usrm.Deleted = false AND u.Deleted = false AND f.Deleted = false", nativeQuery = true) + List getAshaListByFacilities(@Param("facilityIDs") List facilityIDs); + + @Query(value = """ + SELECT asm.supervisorUserID + FROM asha_supervisor_mapping asm + WHERE asm.ashaUserID = :ashaUserID + AND asm.deleted = false + LIMIT 1 + """, nativeQuery = true) + Integer getSupervisorUserIdByAshaId(@Param("ashaUserID") Integer ashaUserID); + // Unclaimed incentive count per ASHA + @Query(value = "SELECT iar.asha_id, " + "COUNT(*) AS unclaimedCount " + "FROM incentive_activity_record iar " + "WHERE iar.asha_id IN (:ashaIds) " + "AND iar.created_date >= :startDate " + "AND iar.created_date < :endDate " + + "AND iar.approval_status = 102 " + + "AND iar.is_default_activity = true " + + "AND (iar.is_claimed = false OR iar.is_claimed IS NULL) " + + "GROUP BY iar.asha_id", + nativeQuery = true) + List getDefaultUnclaimedCountByAshaIds( + @Param("ashaIds") List ashaIds, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate + ); + @Query(value = "SELECT iar.asha_id, " + + "COUNT(*) AS unclaimedCount " + + "FROM incentive_activity_record iar " + + "WHERE iar.asha_id IN (:ashaIds) " + + "AND iar.created_date >= :startDate " + + "AND iar.created_date < :endDate " + + "AND iar.approval_status = 102 " + "AND (iar.is_claimed = false OR iar.is_claimed IS NULL) " + "GROUP BY iar.asha_id", nativeQuery = true) List getUnclaimedCountByAshaIds( @Param("ashaIds") List ashaIds, @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate + ); + + + @Query(value = "SELECT COUNT(*) " + + "FROM incentive_activity_record iar " + + "WHERE iar.asha_id = :ashaId " + + "AND iar.created_date >= :startDate " + + "AND iar.created_date < :endDate " + + "AND (iar.is_claimed = false OR iar.is_claimed IS NULL)", + nativeQuery = true) + Long getUnclaimedCountByAshaId( + @Param("ashaId") Integer ashaId, + @Param("startDate") Timestamp startDate, @Param("endDate") Timestamp endDate); // Get facility details with geo names @@ -78,7 +139,8 @@ List getUnclaimedCountByAshaIds( + "COUNT(*) AS totalRecords, " + "SUM(CASE WHEN iar.approval_status = 101 THEN 1 ELSE 0 END) AS verified, " + "SUM(CASE WHEN iar.approval_status = 103 THEN 1 ELSE 0 END) AS rejected, " - + "SUM(CASE WHEN iar.approval_status = 102 THEN 1 ELSE 0 END) AS pending " + + "SUM(CASE WHEN iar.approval_status = 102 THEN 1 ELSE 0 END) AS pending, " + + "SUM(CASE WHEN iar.approval_status IN (101, 105) THEN 1 ELSE 0 END) AS approved " + "FROM incentive_activity_record iar " + "WHERE iar.asha_id IN (:ashaIds) " + "AND iar.created_date >= :startDate " @@ -91,6 +153,102 @@ List getIncentiveStatusByAshaIds( @Param("startDate") Timestamp startDate, @Param("endDate") Timestamp endDate); + + + @Query(value = "SELECT iar.asha_id, " + + "COUNT(*) AS totalRecords, " + + "SUM(CASE WHEN iar.approval_status = 101 THEN 1 ELSE 0 END) AS verified, " + + "SUM(CASE WHEN iar.approval_status = 103 THEN 1 ELSE 0 END) AS rejected, " + + "SUM(CASE WHEN iar.approval_status = 102 THEN 1 ELSE 0 END) AS pending, " + + "SUM(CASE WHEN iar.approval_status IN (102,105) THEN 1 ELSE 0 END) AS approved " + + "FROM incentive_activity_record iar " + + "WHERE iar.asha_id IN (:ashaIds) " + + "AND iar.created_date >= :startDate " + + "AND iar.is_claimed = true " + + "AND iar.created_date < :endDate " + + "GROUP BY iar.asha_id", + nativeQuery = true) + List getIncentiveStatusByAshaIdsForAnm( + @Param("ashaIds") List ashaIds, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate); + + @Query(value = "SELECT iar.asha_id, " + + "COUNT(*) AS totalRecords, " + + "SUM(CASE WHEN iar.approval_status = 101 THEN 1 ELSE 0 END) AS verified, " + + "SUM(CASE WHEN iar.approval_status = 103 THEN 1 ELSE 0 END) AS rejected, " + + "SUM(CASE WHEN iar.approval_status = 102 THEN 1 ELSE 0 END) AS pending, " + + "SUM(CASE WHEN iar.approval_status IN (101, 105) THEN 1 ELSE 0 END) AS approved " + + "FROM incentive_activity_record iar " + + "WHERE iar.asha_id IN (:ashaIds) " + + "AND iar.created_date >= :startDate " + + "AND iar.is_claimed = true " + + "AND iar.is_default_activity = true " + + "AND iar.is_default_activity = true " + + "AND iar.is_approved = false " + + "AND iar.created_date < :endDate " + + "GROUP BY iar.asha_id", + nativeQuery = true) + List getDefaultIncentiveStatusByAshaIds( + @Param("ashaIds") List ashaIds, + @Param("startDate") Timestamp startDate, + @Param("endDate") Timestamp endDate); + + +// @Query(value = """ +// SELECT +// iar.asha_id, +// COUNT(*) AS totalRecords, +// +// SUM( +// CASE +// WHEN iar.approval_status = 101 +// OR ( +// iar.approval_status = 105 +// AND iar.is_approved = true +// ) +// THEN 1 ELSE 0 +// END +// ) AS verified, +// +// SUM( +// CASE +// WHEN iar.approval_status = 103 +// THEN 1 ELSE 0 +// END +// ) AS rejected, +// +// SUM( +// CASE +// WHEN iar.approval_status = 102 +// THEN 1 ELSE 0 +// END +// ) AS pending, +// +// SUM( +// CASE +// WHEN iar.approval_status IN (101, 105) +// THEN 1 ELSE 0 +// END +// ) AS approved +// +// FROM incentive_activity_record iar +// +// WHERE iar.asha_id IN (:ashaIds) +// AND iar.created_date >= :startDate +// AND iar.created_date < :endDate +// AND iar.is_claimed = true +// AND iar.is_default_activity = true +// +// GROUP BY iar.asha_id +// """, +// nativeQuery = true) +// List getDefaultIncentiveStatusByAshaIds( +// @Param("ashaIds") List ashaIds, +// @Param("startDate") Timestamp startDate, +// @Param("endDate") Timestamp endDate + //); + // Incentive activity history per ASHA (recent records) @Query(value = "SELECT iar.asha_id, iar.name AS activityName, " + "iar.amount, iar.is_eligible, iar.created_date " diff --git a/src/main/java/com/iemr/flw/repo/iemr/TBScreeningRepo.java b/src/main/java/com/iemr/flw/repo/iemr/TBScreeningRepo.java index f656dc91d..cfb0665db 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/TBScreeningRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/TBScreeningRepo.java @@ -5,6 +5,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryHints; +import jakarta.persistence.QueryHint; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -37,6 +39,8 @@ public interface TBScreeningRepo extends JpaRepository { @Query("SELECT tbs FROM TBScreening tbs WHERE tbs.providerServiceMapID = :psmId AND tbs.deleted = false ORDER BY tbs.visitDate DESC") List findAllByProviderServiceMapID(@Param("psmId") Integer psmId); + // Read-only: getAll only reads these rows, so Hibernate keeps no change-tracking snapshot per row + @QueryHints(@QueryHint(name = "org.hibernate.readOnly", value = "true")) @Query("SELECT tbs FROM TBScreening tbs WHERE tbs.benRegID IN " + "(SELECT b.beneficiaryRegID FROM BenFlowStatus b WHERE b.providerServiceMapId = :psmId AND b.villageID = :villageId) " + "AND tbs.deleted = false ORDER BY tbs.visitDate DESC") diff --git a/src/main/java/com/iemr/flw/repo/iemr/TBSuspectedRepo.java b/src/main/java/com/iemr/flw/repo/iemr/TBSuspectedRepo.java index 3df494fa4..277d21fd6 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/TBSuspectedRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/TBSuspectedRepo.java @@ -29,8 +29,11 @@ TBSuspected getByUserIdAndBenIdAndVisitCode(@Param("benId") Long benId, @Param(" @Query("SELECT tbs FROM TBSuspected tbs WHERE tbs.userId = :userId and tbs.visitDate >= :fromDate and tbs.visitDate <= :toDate") List getByUserId(@Param("userId") Integer userId, @Param("fromDate") Timestamp fromDate, @Param("toDate") Timestamp toDate); - @Query("SELECT tbs FROM TBSuspected tbs WHERE tbs.benId IN " + - "(SELECT b.beneficiaryID FROM BenFlowStatus b WHERE b.providerServiceMapId = :psmId AND b.villageID = :villageId)") + // Same query as before in SQL, with an optimizer hint so the village subquery reads + // idx_flow_psm_village_reg instead of the PSM-only index (ignored if that index is missing). + @Query(nativeQuery = true, value = "SELECT t.* FROM db_iemr.tb_suspected t WHERE t.ben_id IN " + + "(SELECT /*+ INDEX(b idx_flow_psm_village_reg) */ b.beneficiary_id FROM db_iemr.i_ben_flow_outreach b " + + "WHERE b.providerServiceMapID = :psmId AND b.villageID = :villageId)") List getByProviderServiceMapIdAndVillageId(@Param("psmId") Integer psmId, @Param("villageId") Integer villageId); @Transactional diff --git a/src/main/java/com/iemr/flw/repo/iemr/TbReferralFollowUpRepo.java b/src/main/java/com/iemr/flw/repo/iemr/TbReferralFollowUpRepo.java new file mode 100644 index 000000000..e41bb6b8f --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/TbReferralFollowUpRepo.java @@ -0,0 +1,15 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.TbReferralFollowUp; +import io.swagger.v3.oas.annotations.info.License; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import javax.swing.*; +import java.util.List; + +@Repository +public interface TbReferralFollowUpRepo extends JpaRepository { + List findByUserId(Integer userId); +} diff --git a/src/main/java/com/iemr/flw/repo/iemr/TbTptFollowUpRepo.java b/src/main/java/com/iemr/flw/repo/iemr/TbTptFollowUpRepo.java new file mode 100644 index 000000000..7e398affb --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/TbTptFollowUpRepo.java @@ -0,0 +1,16 @@ +package com.iemr.flw.repo.iemr; + +import com.iemr.flw.domain.iemr.TbReferralFollowUp; +import com.iemr.flw.domain.iemr.TbTptFollowUp; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.sql.Timestamp; +import java.util.List; + +@Repository +public interface TbTptFollowUpRepo extends JpaRepository { + List findByUserId(Integer userId); + + List findByBenIdAndRegimenTypeAndTreatmentStartDate(Long benId, String regimenType, Timestamp treatmentStartDate); +} diff --git a/src/main/java/com/iemr/flw/repo/iemr/UserFcmTokenRepo.java b/src/main/java/com/iemr/flw/repo/iemr/UserFcmTokenRepo.java new file mode 100644 index 000000000..3426f5a25 --- /dev/null +++ b/src/main/java/com/iemr/flw/repo/iemr/UserFcmTokenRepo.java @@ -0,0 +1,34 @@ +/* +* 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.repo.iemr; + +import com.iemr.flw.domain.iemr.UserFcmTokenData; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserFcmTokenRepo extends JpaRepository { + UserFcmTokenData findByUserId(Integer userId); +} diff --git a/src/main/java/com/iemr/flw/repo/iemr/UserServiceRoleRepo.java b/src/main/java/com/iemr/flw/repo/iemr/UserServiceRoleRepo.java index b94550a28..95733b8a6 100644 --- a/src/main/java/com/iemr/flw/repo/iemr/UserServiceRoleRepo.java +++ b/src/main/java/com/iemr/flw/repo/iemr/UserServiceRoleRepo.java @@ -60,6 +60,11 @@ public interface UserServiceRoleRepo extends JpaRepository getNikshayMappingRows(@Param("userId") Integer userId, @Param("providerServiceMapId") Integer providerServiceMapId); + // Every active mapping row's service and village list, for the STOP-TB changes feed's scope check. + @Query(value = "SELECT usrm.ProviderServiceMapID, usrm.Villageid FROM m_userservicerolemapping usrm " + + "WHERE usrm.UserID = :userId AND usrm.Deleted = false", nativeQuery = true) + List getServiceVillageRows(@Param("userId") Integer userId); + @Query(value = "SELECT NikshayTUID, TUName FROM m_nikshay_tu WHERE NikshayTUID IN (:ids)", nativeQuery = true) List findNikshayTuNames(@Param("ids") java.util.Collection ids); @@ -74,4 +79,10 @@ List getNikshayMappingRows(@Param("userId") Integer userId, @Query(value = "SELECT DistrictName FROM m_nikshay_district WHERE NikshayDistrictID = :id", nativeQuery = true) String findNikshayDistrictName(@Param("id") Integer id); + @Query("SELECT new com.iemr.flw.dto.iemr.UserServiceRoleDTO(u.userId, u.name, u.userName, u.stateId, u.stateName, u.workingDistrictId," + + "u.workingDistrictName, u.serviceProviderId, u.roleId, u.roleName, u.providerServiceMapId, u.blockid, u.blockname, u.villageid, " + + "u.villagename) FROM UserServiceRole u WHERE u.userId IN :userIds " + + "AND u.userServciceRoleDeleted = false AND u.userDeleted = false") + List getUserRolesByIds(@Param("userIds") List userIds); + } 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..7d30fe25e --- /dev/null +++ b/src/main/java/com/iemr/flw/service/BadgeService.java @@ -0,0 +1,85 @@ +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(), e.getAwardKey())) + .toList(); + } + + /** + * 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; + long now = System.currentTimeMillis(); + int inserted = 0; + for (BadgeEarnedDTO dto : badges) { + if (dto == null || dto.getBadgeId() == null || dto.getLevel() == null) 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); + inserted++; + } + return inserted; + } +} diff --git a/src/main/java/com/iemr/flw/service/BeneficiaryService.java b/src/main/java/com/iemr/flw/service/BeneficiaryService.java index e69bc887f..d043e7eea 100644 --- a/src/main/java/com/iemr/flw/service/BeneficiaryService.java +++ b/src/main/java/com/iemr/flw/service/BeneficiaryService.java @@ -5,11 +5,14 @@ import com.iemr.flw.dto.iemr.SAMResponseDTO; import java.util.List; +import java.util.Map; public interface BeneficiaryService { String getBenData(GetBenRequestHandler requestDTO, String authorisation) throws Exception; + Map getBeneficiaryRecordsByRegIds(List benRegIds, String authorisation); + String saveEyeCheckupVsit(List eyeCheckupRequestDTOS,String token); List getEyeCheckUpVisit(GetBenRequestHandler request,String token); diff --git a/src/main/java/com/iemr/flw/service/CampConfigService.java b/src/main/java/com/iemr/flw/service/CampConfigService.java index 305bf1f05..325b0dabf 100644 --- a/src/main/java/com/iemr/flw/service/CampConfigService.java +++ b/src/main/java/com/iemr/flw/service/CampConfigService.java @@ -1,8 +1,14 @@ package com.iemr.flw.service; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.List; + /** * Camp/van identity for this deployment. * @@ -17,12 +23,14 @@ * No inline default — every properties file must set stoptb.van.id explicitly, so a forgotten * config fails loudly at startup instead of silently running unconfigured. * - * Scope: vanID (and the VanSerialNo stamping that depends on it) only. parkingPlaceID is out of - * scope for this change. + * The van's parking place and store (facility) come from m_van, the same source MMU uses + * (MasterVanRepo.getFacilityID), and are read once and cached since the van never changes. */ @Service public class CampConfigService { + private final Logger logger = LoggerFactory.getLogger(CampConfigService.class); + @Value("${stoptb.van.id}") private int vanID; @@ -31,6 +39,12 @@ public class CampConfigService { @Value("${stoptb.enforce.vanid}") private boolean enforceVanID; + @PersistenceContext(unitName = "db_iemr") + private EntityManager entityManager; + + private volatile Integer parkingPlaceID; + private volatile Integer facilityID; + public Integer getVanID() { if (vanID <= 0) { if (enforceVanID) { @@ -42,13 +56,51 @@ public Integer getVanID() { return vanID; } + /** + * The configured van (stoptb.van.id) is always used. A vanID sent by the app is ignored, so data and + * stock never land on another camp's van/store; a different value is only logged. + */ + public Integer resolveVanID(Integer requestedVanID) { + Integer configured = getVanID(); + if (requestedVanID != null && requestedVanID > 0 && !requestedVanID.equals(configured)) { + logger.warn("Ignoring vanID " + requestedVanID + " from request; using stoptb.van.id " + configured); + } + return configured; + } + public boolean isCampConfigured() { return vanID > 0; } - // Not sourced from Redis anymore (out of scope for this change) — kept only because - // callers across StopTBServiceImpl/DiagnosticOrderServiceImpl/etc. still call it. + // Parking place of the configured van (m_van.ParkingPlaceID); 0 when the camp is not configured + // or the van is not found, which is what callers received before this lookup existed. public Integer getParkingPlaceID() { - return 0; + if (parkingPlaceID == null) loadVanDetails(); + return parkingPlaceID != null ? parkingPlaceID : 0; + } + + // Store mapped to the configured van (m_van.FacilityID); null when no store is mapped. + public Integer getFacilityID() { + if (facilityID == null) loadVanDetails(); + return facilityID; + } + + private synchronized void loadVanDetails() { + if (!isCampConfigured() || (parkingPlaceID != null && facilityID != null)) return; + try { + List rows = entityManager.createNativeQuery( + "SELECT ParkingPlaceID, FacilityID FROM db_iemr.m_van WHERE VanID = :vanID AND Deleted = false") + .setParameter("vanID", vanID) + .getResultList(); + if (rows.isEmpty()) { + logger.warn("stoptb.van.id " + vanID + " not found in m_van"); + return; + } + Object[] row = (Object[]) rows.get(0); + parkingPlaceID = row[0] != null ? ((Number) row[0]).intValue() : null; + facilityID = row[1] != null ? ((Number) row[1]).intValue() : null; + } catch (Exception e) { + logger.error("Cannot read m_van details for vanID " + vanID, e); + } } } diff --git a/src/main/java/com/iemr/flw/service/ChildCareService.java b/src/main/java/com/iemr/flw/service/ChildCareService.java index c78de3fdc..a7e526e69 100644 --- a/src/main/java/com/iemr/flw/service/ChildCareService.java +++ b/src/main/java/com/iemr/flw/service/ChildCareService.java @@ -16,6 +16,7 @@ public interface ChildCareService { List getHBNCDetails(GetBenRequestHandler dto); String saveHBNCDetails(List hbncRequestDTOs,Integer userId); + String saveHBNCDetails(List hbncRequestDTOs); List getChildVaccinationDetails(GetBenRequestHandler dto); diff --git a/src/main/java/com/iemr/flw/service/DiagnosticOrderService.java b/src/main/java/com/iemr/flw/service/DiagnosticOrderService.java index 250aa143b..42fb1c337 100644 --- a/src/main/java/com/iemr/flw/service/DiagnosticOrderService.java +++ b/src/main/java/com/iemr/flw/service/DiagnosticOrderService.java @@ -23,8 +23,18 @@ public interface DiagnosticOrderService { DiagnosticOrderResultDto processResult(DiagnosticOrder order, DiagnosticPollResult result) throws Exception; + /** + * Polls the vendor once for this order. The order is re-read first and skipped (returns null) if it + * is no longer PENDING/IN_PROGRESS, e.g. a manual result landed after the scheduler loaded it. + */ DiagnosticPollResult pollOnce(DiagnosticOrder order) throws Exception; + /** + * Daily cutoff: marks this order CLOSED with the given reason and notifies the vendor, unless it was + * resolved (e.g. by a manual result) after the scheduler loaded it. + */ + void expireOrder(DiagnosticOrder order, String reason) throws Exception; + DiagnosticOrderResultDto triggerManualPoll(Long beneficiaryId, String orderType, Long visitCode) throws Exception; DiagnosticOrder retryPoll(Long beneficiaryId, String orderType, Long visitCode) throws Exception; @@ -38,6 +48,14 @@ public interface DiagnosticOrderService { DiagnosticOrderResultDto getOrderResult(Long beneficiaryId, String orderType, Long visitCode); + /** + * Orders with their result, keyed by order id (STOP-TB changes feed). Deleted orders are left + * out. Each record carries what getBeneficiariesByStatus and getOrderResult give today: the + * beneficiary, order type, visit code and status (the app buckets by the latest order per + * beneficiary and type), plus the result fields. + */ + java.util.Map> getOrderRecordsByIds(java.util.Collection ids); + DiagnosticOrder getOrder(Long beneficiaryId, String orderType, Long visitCode) throws Exception; List getOrdersByBeneficiaryId(Long beneficiaryId) throws Exception; diff --git a/src/main/java/com/iemr/flw/service/DynamicFormResponseService.java b/src/main/java/com/iemr/flw/service/DynamicFormResponseService.java index 1f2255f3e..8265cfafb 100644 --- a/src/main/java/com/iemr/flw/service/DynamicFormResponseService.java +++ b/src/main/java/com/iemr/flw/service/DynamicFormResponseService.java @@ -55,6 +55,12 @@ List getResponsesByBeneficiary(Long beneficiaryId, Integer vill /** Single response with all nested section and question answers. */ FormResponseDTO getResponseById(Long responseId); + /** + * Responses with all nested answers, keyed by responseId, as getResponsesByBeneficiary returns + * them (STOP-TB changes feed). Ids with no response are left out. + */ + java.util.Map getResponsesByIds(java.util.Collection responseIds); + /** * Submit multiple form responses in one bulk transaction. * All FormResponse rows are batch-inserted via saveAll() so responseIds are available diff --git a/src/main/java/com/iemr/flw/service/IncentiveLogicService.java b/src/main/java/com/iemr/flw/service/IncentiveLogicService.java index 8becce199..0c86ab7be 100644 --- a/src/main/java/com/iemr/flw/service/IncentiveLogicService.java +++ b/src/main/java/com/iemr/flw/service/IncentiveLogicService.java @@ -32,4 +32,6 @@ public interface IncentiveLogicService { IncentiveActivityRecord incentiveForGiveingIFA(Long benId, Timestamp treatmentStartDate, Timestamp treatmentEndDate, Integer userId); IncentiveActivityRecord incentiveForTbSuspected(Long benId, Timestamp treatmentStartDate, Timestamp treatmentEndDate, Integer userId); + + IncentiveActivityRecord incentiveForTbPreventiveFollowUp(Long benId, Timestamp completionDate, Timestamp completionDate1, Integer userId); } diff --git a/src/main/java/com/iemr/flw/service/IncentiveService.java b/src/main/java/com/iemr/flw/service/IncentiveService.java index 8b2e46001..ad7c122c9 100644 --- a/src/main/java/com/iemr/flw/service/IncentiveService.java +++ b/src/main/java/com/iemr/flw/service/IncentiveService.java @@ -14,7 +14,7 @@ public interface IncentiveService { String getIncentiveMaster(IncentiveRequestDTO incentiveRequestDTO); String getAllIncentivesByUserId(GetBenRequestHandler requestDTO); - String getAllIncentivesGroupedSummary(GetBenRequestHandler requestDTO); + String getAllIncentivesGroupedSummary(GetBenRequestHandler requestDTO,Integer userId); String getAllIncentivesGroupedActivity(GetBenRequestHandler requestDTO); String updateIncentive(PendingActivityDTO pendingActivityDTO); @@ -22,4 +22,4 @@ public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boolean isClaimed, - String token);} + String token,String incentiveId);} diff --git a/src/main/java/com/iemr/flw/service/NotificationService.java b/src/main/java/com/iemr/flw/service/NotificationService.java new file mode 100644 index 000000000..4d561b517 --- /dev/null +++ b/src/main/java/com/iemr/flw/service/NotificationService.java @@ -0,0 +1,14 @@ +package com.iemr.flw.service; + +import com.iemr.flw.dto.iemr.NotificationListDTO; + +public interface NotificationService { + public String sendNotification(String appType, String topic, String title, String body, String redirect,String notificationType,Integer reciverID); + NotificationListDTO getNotifications(Integer receiverUserId); + + Long getUnreadCount(Integer receiverUserId); + + boolean markAsRead(Long notificationId); + + int markAllAsRead(Integer receiverUserId); +} diff --git a/src/main/java/com/iemr/flw/service/StopTBPrescriptionService.java b/src/main/java/com/iemr/flw/service/StopTBPrescriptionService.java new file mode 100644 index 000000000..c61b4d4f9 --- /dev/null +++ b/src/main/java/com/iemr/flw/service/StopTBPrescriptionService.java @@ -0,0 +1,721 @@ +package com.iemr.flw.service; + +import com.iemr.flw.domain.identity.RMNCHMBeneficiarydetail; +import com.iemr.flw.domain.identity.RMNCHMBeneficiarymapping; +import com.iemr.flw.domain.iemr.BenVisitDetail; +import com.iemr.flw.repo.identity.BeneficiaryRepo; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.Period; +import java.util.*; + +/** + * Stop TB nurse OPD: chief complaints, prescription and dispensing written to the same standard + * tables MMU/TM and Inventory-API use, inside the caller's transaction. + * + * - Chief complaints -> t_benchiefcomplaint (one row per complaint, like TM saveBenChiefComplaints) + * - Prescription -> t_prescription + t_prescribeddrug (DrugID + QtyPrescribed, like TM doctor save) + * - Dispensing -> t_patientissue + t_itemstockexit (ExitType T_PatientIssue) and + * t_itemstockentry.QuantityInHand reduced, like Inventory-API issuePatientDrugs. + * + * Batches are picked by m_itemcategory.IssueType (FEFO / FIFO / LIFO) from non-expired batches that + * last the treatment duration, and are locked (SELECT ... FOR UPDATE) so two nurses can never take the + * same stock. If any drug is short, nothing is saved (InsufficientStockException rolls back the caller). + * + * Native SQL only, no new entity mappings: FLW runs with ddl-auto=create. + */ +@Service +public class StopTBPrescriptionService { + + private static final String REFERENCE_PREFIX = "StopTB:"; + + @PersistenceContext(unitName = "db_iemr") + private EntityManager em; + + @Autowired + private CampConfigService campConfigService; + + @Autowired + private BeneficiaryRepo beneficiaryRepo; + + // Off by default: save the prescription only (no store/stock check, no patient issue, stock unchanged). + // true = check stock and dispense from the camp store. + @Value("${stoptb.dispense.enabled:false}") + private boolean dispenseEnabled; + + private final Logger logger = LoggerFactory.getLogger(StopTBPrescriptionService.class); + + public static class InsufficientStockException extends RuntimeException { + public InsufficientStockException(String message) { + super(message); + } + } + + /** Previous result for a submissionId already saved, or null. Lets the app retry safely. */ + @Transactional(propagation = Propagation.MANDATORY) + public Map findPreviousSubmission(Long beneficiaryRegID, String submissionId) { + if (submissionId == null || submissionId.isBlank()) return null; + List rows = em.createNativeQuery("SELECT PatientIssueID, VisitCode, PrescriptionID FROM db_iemr.t_patientissue " + + "WHERE BeneficiaryRegID = :ben AND Reference = :ref AND Deleted = false") + .setParameter("ben", beneficiaryRegID) + .setParameter("ref", REFERENCE_PREFIX + submissionId) + .getResultList(); + if (rows.isEmpty()) return null; + Object[] row = (Object[]) rows.get(0); + Integer facilityID = campConfigService.getFacilityID(); + + List issued = em.createNativeQuery("SELECT e.ItemID, i.ItemName, SUM(x.Quantity) " + + "FROM db_iemr.t_itemstockexit x " + + "JOIN db_iemr.t_itemstockentry e ON e.VanSerialNo = x.ItemStockEntryID AND e.FacilityID = x.FacilityID " + + "JOIN db_iemr.m_item i ON i.ItemID = e.ItemID " + + "WHERE x.ExitType = 'T_PatientIssue' AND x.ExitTypeID = :issueID AND x.FacilityID = :facilityID " + + "GROUP BY e.ItemID, i.ItemName") + .setParameter("issueID", ((Number) row[0]).longValue()) + .setParameter("facilityID", facilityID) + .getResultList(); + List> dispensed = new ArrayList<>(); + for (Object o : issued) { + Object[] r = (Object[]) o; + int drugID = ((Number) r[0]).intValue(); + Map d = new LinkedHashMap<>(); + d.put("drugID", drugID); + d.put("drugName", r[1]); + d.put("qtyIssued", ((Number) r[2]).intValue()); + d.put("availableQuantity", availableQuantity(facilityID, drugID, 0)); + dispensed.add(d); + } + Map result = new LinkedHashMap<>(); + result.put("beneficiaryRegID", beneficiaryRegID); + result.put("visitCode", row[1] != null ? ((Number) row[1]).longValue() : null); + result.put("prescriptionID", row[2] != null ? ((Number) row[2]).longValue() : null); + result.put("dispensed", dispensed); + result.put("duplicate", true); + return result; + } + + /** + * Replaces the visit's chief complaints with the given list (edit = remove old + insert new, as MMU + * does on update). Rows without an ID and without a name are skipped. + */ + @Transactional(propagation = Propagation.MANDATORY) + public void saveChiefComplaints(List> complaints, Long beneficiaryRegID, BenVisitDetail visit, + String createdBy, Integer vanID, Integer parkingPlaceID) { + if (complaints == null) return; + // Processed='N' so the removal is uploaded to central again (central updates rows it already has). + em.createNativeQuery("UPDATE db_iemr.t_benchiefcomplaint SET Deleted = true, Processed = 'N', ModifiedBy = :user " + + "WHERE BeneficiaryRegID = :ben AND VisitCode = :visitCode AND Deleted = false") + .setParameter("user", createdBy) + .setParameter("ben", beneficiaryRegID) + .setParameter("visitCode", visit.getVisitCode()) + .executeUpdate(); + for (Map c : complaints) { + Integer complaintID = toInt(c.get("chiefComplaintID")); + String complaint = toStr(c.get("chiefComplaint")); + // ID from the chief complaint master is required: getAll and reports identify new records by it. + if (complaintID == null) { + throw new IllegalArgumentException("chiefComplaintID is required for every chief complaint" + + (complaint != null ? " (" + complaint + ")" : "")); + } + em.createNativeQuery("INSERT INTO db_iemr.t_benchiefcomplaint (BeneficiaryRegID, BenVisitID, ProviderServiceMapID, " + + "VisitCode, ChiefComplaintID, ChiefComplaint, Duration, UnitOfDuration, Description, Deleted, " + + "Processed, CreatedBy, VanID, ParkingPlaceID) VALUES (:ben, :visitID, :psm, :visitCode, :cid, " + + ":name, :duration, :unit, :description, false, 'N', :user, :van, :pp)") + .setParameter("ben", beneficiaryRegID) + .setParameter("visitID", visit.getBenVisitId()) + .setParameter("psm", visit.getProviderServiceMapID()) + .setParameter("visitCode", visit.getVisitCode()) + .setParameter("cid", complaintID) + .setParameter("name", complaint) + .setParameter("duration", toInt(c.get("duration"))) + .setParameter("unit", toStr(c.get("unitOfDuration"))) + .setParameter("description", toStr(c.get("description"))) + .setParameter("user", createdBy) + .setParameter("van", vanID) + .setParameter("pp", parkingPlaceID) + .executeUpdate(); + stampVanSerialNo("t_benchiefcomplaint", "ID", lastInsertId()); + } + } + + /** + * Saves the prescription and issues every drug from the camp store. Returns prescriptionID and, + * per drug, the quantity issued and what is left in the store. + */ + @Transactional(propagation = Propagation.MANDATORY) + public Map prescribeAndDispense(Map prescription, Long beneficiaryRegID, + BenVisitDetail visit, String submissionId, String createdBy, Integer vanID, Integer parkingPlaceID) { + List> drugs = toList(prescription.get("drugs")); + if (!dispenseEnabled) { + return prescribeWithoutDispensing(prescription, drugs, beneficiaryRegID, visit, createdBy, vanID, + parkingPlaceID); + } + Map result = new LinkedHashMap<>(); + if (drugs.isEmpty()) { + // Remarks without medicine are still kept, as MMU saves the visit instruction without drugs. + String instruction = toStr(prescription.get("instruction")); + if (instruction != null && !instruction.isBlank()) { + result.put("prescriptionID", insertPrescription(beneficiaryRegID, visit, instruction, createdBy, vanID, + parkingPlaceID)); + } + return result; + } + + Integer facilityID = campConfigService.getFacilityID(); + if (facilityID == null) { + throw new IllegalStateException("No store is mapped to van " + vanID + " (m_van.FacilityID)"); + } + + // 1. Validate every line and check total stock per drug before writing anything. + Map requested = new LinkedHashMap<>(); + Map items = new HashMap<>(); + for (Map d : drugs) { + Integer drugID = toInt(d.get("drugID")); + if (drugID == null) throw new IllegalArgumentException("drugID is required for every drug"); + if (!items.containsKey(drugID)) items.put(drugID, getStoreItem(facilityID, drugID)); + Integer qty = toInt(d.get("qtyPrescribed")); + if (qty == null || qty <= 0) { + // No quantity from the app: tablets/capsules = doses per day x days, other forms = 1. + qty = calculateQuantity(d, toInt(items.get(drugID)[7])); + if (qty == null) { + throw new IllegalArgumentException("qtyPrescribed is required for drugID " + drugID + + " (tablet/capsule quantity needs frequency and duration)"); + } + d.put("qtyPrescribed", qty); // saved in t_prescribeddrug.QtyPrescribed + } + requested.merge(drugID, qty, Integer::sum); + } + List shortages = new ArrayList<>(); + Map> lockedBatches = new HashMap<>(); + for (Map.Entry r : requested.entrySet()) { + Object[] item = items.get(r.getKey()); + int durationDays = maxDurationDays(drugs, r.getKey()); + List batches = lockBatches(facilityID, r.getKey(), (String) item[6], durationDays); + lockedBatches.put(r.getKey(), batches); + int available = batches.stream().mapToInt(b -> ((Number) b[1]).intValue()).sum(); + if (available < r.getValue()) { + shortages.add(item[1] + " (drugID " + r.getKey() + "): asked " + r.getValue() + ", available " + available); + } + } + if (!shortages.isEmpty()) { + throw new InsufficientStockException("Insufficient stock - " + String.join("; ", shortages)); + } + + // 2. Prescription + prescribed drugs (Stop TB service line, same as the visit). + Integer storePsm = getStorePsm(facilityID); + long prescriptionID = insertPrescription(beneficiaryRegID, visit, toStr(prescription.get("instruction")), + createdBy, vanID, parkingPlaceID); + + for (Map d : drugs) { + Integer drugID = toInt(d.get("drugID")); + Object[] item = items.get(drugID); + em.createNativeQuery("INSERT INTO db_iemr.t_prescribeddrug (BeneficiaryRegID, BenVisitID, ProviderServiceMapID, " + + "VisitCode, PrescriptionID, DrugForm, DrugID, GenericDrugName, DrugStrength, Dose, Route, Frequency, " + + "Duration, DuartionUnit, SpecialInstruction, QtyPrescribed, isEDL, Deleted, Processed, CreatedBy, " + + "VanID, ParkingPlaceID) VALUES (:ben, :visitID, :psm, :visitCode, :prescriptionID, :form, :drugID, " + + ":name, :strength, :dose, :route, :frequency, :duration, :durationUnit, :instruction, :qty, " + + ":isEDL, false, 'N', :user, :van, :pp)") + .setParameter("ben", beneficiaryRegID) + .setParameter("visitID", visit.getBenVisitId()) + .setParameter("psm", visit.getProviderServiceMapID()) + .setParameter("visitCode", visit.getVisitCode()) + .setParameter("prescriptionID", prescriptionID) + .setParameter("form", item[3]) + .setParameter("drugID", drugID) + .setParameter("name", item[1]) + .setParameter("strength", item[2]) + .setParameter("dose", toStr(d.get("dose"))) + .setParameter("route", item[4]) + .setParameter("frequency", toStr(d.get("frequency"))) + .setParameter("duration", toStr(d.get("duration"))) + .setParameter("durationUnit", toStr(d.get("durationUnit"))) + .setParameter("instruction", toStr(d.get("instructions"))) + .setParameter("qty", toInt(d.get("qtyPrescribed"))) + .setParameter("isEDL", item[5]) + .setParameter("user", createdBy) + .setParameter("van", vanID) + .setParameter("pp", parkingPlaceID) + .executeUpdate(); + stampVanSerialNo("t_prescribeddrug", "PrescribedDrugID", lastInsertId()); + } + + // 3. Patient issue header (same shape as Inventory-API issuePatientDrugs, IssueType 'System'). + Object[] patient = getPatientDetails(beneficiaryRegID); + em.createNativeQuery("INSERT INTO db_iemr.t_patientissue (BeneficiaryRegID, BenVisitID, VisitCode, FacilityID, " + + "PatientName, Age, Gender, PrescriptionID, Reference, IssueType, IssuedBy, ProviderServiceMapID, " + + "Deleted, Processed, CreatedBy, SyncFacilityID, VanID, ParkingPlaceID) VALUES (:ben, :visitID, " + + ":visitCode, :facilityID, :name, :age, :gender, :prescriptionID, :ref, 'System', :user, :psm, false, " + + "'N', :user, :facilityID, :van, :pp)") + .setParameter("ben", beneficiaryRegID) + .setParameter("name", patient[0]) + .setParameter("age", patient[1]) + .setParameter("gender", patient[2]) + .setParameter("visitID", visit.getBenVisitId()) + .setParameter("visitCode", visit.getVisitCode()) + .setParameter("facilityID", facilityID) + .setParameter("prescriptionID", prescriptionID) + .setParameter("ref", submissionId != null && !submissionId.isBlank() ? REFERENCE_PREFIX + submissionId : null) + .setParameter("user", createdBy) + .setParameter("psm", storePsm) + .setParameter("van", vanID) + .setParameter("pp", parkingPlaceID) + .executeUpdate(); + long patientIssueID = lastInsertId(); + stampVanSerialNo("t_patientissue", "PatientIssueID", patientIssueID); + + // 4. Take each drug from its locked batches in issue order; one stock exit per batch used. + List> dispensed = new ArrayList<>(); + for (Map.Entry r : requested.entrySet()) { + int remaining = r.getValue(); + for (Object[] batch : lockedBatches.get(r.getKey())) { + if (remaining == 0) break; + long batchSerialNo = ((Number) batch[0]).longValue(); + int take = Math.min(remaining, ((Number) batch[1]).intValue()); + em.createNativeQuery("INSERT INTO db_iemr.t_itemstockexit (ItemStockEntryID, FacilityID, Quantity, " + + "ProviderServiceMapID, ExitTypeID, ExitType, Deleted, Processed, CreatedBy, SyncFacilityID, " + + "VanID, ParkingPlaceID) VALUES (:batch, :facilityID, :qty, :psm, :issueID, 'T_PatientIssue', " + + "false, 'N', :user, :facilityID, :van, :pp)") + .setParameter("batch", batchSerialNo) + .setParameter("facilityID", facilityID) + .setParameter("qty", take) + .setParameter("psm", storePsm) + .setParameter("issueID", patientIssueID) + .setParameter("user", createdBy) + .setParameter("van", vanID) + .setParameter("pp", parkingPlaceID) + .executeUpdate(); + stampVanSerialNo("t_itemstockexit", "ItemStockExitID", lastInsertId()); + // Same update as Inventory-API ItemStockEntryRepo.updateStock. Processed='N' sends the batch's + // new stock to central on the next upload; central updates its copy by VanSerialNo + store. + em.createNativeQuery("UPDATE db_iemr.t_itemstockentry SET QuantityInHand = QuantityInHand - :qty, " + + "Processed = 'N' WHERE VanSerialNo = :batch AND FacilityID = :facilityID") + .setParameter("qty", take) + .setParameter("batch", batchSerialNo) + .setParameter("facilityID", facilityID) + .executeUpdate(); + remaining -= take; + } + Map d = new LinkedHashMap<>(); + d.put("drugID", r.getKey()); + d.put("drugName", items.get(r.getKey())[1]); + d.put("qtyIssued", r.getValue()); + d.put("availableQuantity", availableQuantity(facilityID, r.getKey(), 0)); + dispensed.add(d); + } + + result.put("prescriptionID", prescriptionID); + result.put("dispensed", dispensed); + return result; + } + + /** + * OPD records for getAll from the standard tables, one per visit that was saved with the new payload + * (complaint with ChiefComplaintID or drug with DrugID). Older visits have only the free-text copy in + * tb_stoptb_general_opd and are added by the caller. Field names and formats match that table's response: + * chiefComplaint = JSON list of names, medication/dosage/frequency/duration = comma separated. + */ + @SuppressWarnings("unchecked") + public List> getStandardOpdRecords(Integer providerServiceMapID, Integer villageID) { + String villageFilter = villageID != null + ? "AND v.BeneficiaryRegID IN (SELECT f.beneficiary_reg_id FROM db_iemr.i_ben_flow_outreach f " + + "WHERE f.providerServiceMapID = :psm AND f.villageID = :villageID) " + : ""; + Map params = new HashMap<>(); + params.put("psm", providerServiceMapID); + if (villageID != null) params.put("villageID", villageID); + return standardOpdRecords("v.ProviderServiceMapID = :psm " + villageFilter, params); + } + + // The same records for specific visits (BenVisitID), for the STOP-TB changes feed. A visit that is + // deleted or has no complaint or drug is left out, exactly as getStandardOpdRecords leaves it out. + public List> getStandardOpdRecordsByVisitIds(Collection benVisitIds) { + if (benVisitIds.isEmpty()) return new ArrayList<>(); + Map params = new HashMap<>(); + params.put("ids", benVisitIds); + return standardOpdRecords("v.BenVisitID IN (:ids) ", params); + } + + // Visit codes among these that getStandardOpdRecords would return (not deleted, with a complaint or drug). + public Set visitCodesWithStandardOpd(Collection visitCodes) { + Set out = new HashSet<>(); + if (visitCodes.isEmpty()) return out; + Map params = new HashMap<>(); + params.put("codes", visitCodes); + for (Map m : standardOpdRecords("v.VisitCode IN (:codes) ", params)) { + out.add((Long) m.get("visitCode")); + } + return out; + } + + private List> standardOpdRecords(String visitFilter, Map params) { + Query visitQuery = em.createNativeQuery("SELECT v.BenVisitID, v.BeneficiaryRegID, v.VisitCode, v.CreatedBy, " + + "v.CreatedDate, v.LastModDate, v.ProviderServiceMapID FROM db_iemr.t_benvisitdetail v " + + "WHERE " + visitFilter + "AND v.Deleted = false " + + "AND (EXISTS (SELECT 1 FROM db_iemr.t_benchiefcomplaint c WHERE c.BeneficiaryRegID = v.BeneficiaryRegID " + + " AND c.VisitCode = v.VisitCode AND c.ChiefComplaintID IS NOT NULL AND c.Deleted = false) " + + " OR EXISTS (SELECT 1 FROM db_iemr.t_prescribeddrug d WHERE d.BeneficiaryRegID = v.BeneficiaryRegID " + + " AND d.VisitCode = v.VisitCode AND d.DrugID IS NOT NULL AND d.Deleted = false)) " + + "ORDER BY v.CreatedDate DESC"); + params.forEach(visitQuery::setParameter); + List visits = visitQuery.getResultList(); + + Map> byVisit = new LinkedHashMap<>(); + for (Object[] v : visits) { + Map m = new LinkedHashMap<>(); + m.put("id", ((Number) v[0]).longValue()); + m.put("beneficiaryRegID", ((Number) v[1]).longValue()); + m.put("visitCode", ((Number) v[2]).longValue()); + m.put("providerServiceMapID", v[6] != null ? ((Number) v[6]).intValue() : null); + m.put("chiefComplaint", new ArrayList()); + m.put("medication", new StringJoiner(", ")); + m.put("dosage", new StringJoiner(", ")); + m.put("frequency", new StringJoiner(", ")); + m.put("duration", new StringJoiner(", ")); + m.put("drugs", new ArrayList>()); + m.put("notes", null); + m.put("createdBy", v[3]); + m.put("createdDate", v[4]); + m.put("updateDate", v[5]); + m.put("updatedBy", null); + byVisit.put(((Number) v[2]).longValue(), m); + } + if (byVisit.isEmpty()) return new ArrayList<>(); + + List visitCodes = new ArrayList<>(byVisit.keySet()); + for (int i = 0; i < visitCodes.size(); i += 500) { + List chunk = visitCodes.subList(i, Math.min(i + 500, visitCodes.size())); + + for (Object o : em.createNativeQuery("SELECT VisitCode, ChiefComplaint, LastModDate FROM db_iemr.t_benchiefcomplaint " + + "WHERE VisitCode IN (:codes) AND ChiefComplaintID IS NOT NULL AND Deleted = false ORDER BY ID") + .setParameter("codes", chunk).getResultList()) { + Object[] r = (Object[]) o; + Map m = byVisit.get(((Number) r[0]).longValue()); + if (r[1] != null) ((List) m.get("chiefComplaint")).add(r[1].toString()); + latest(m, r[2]); + } + + for (Object o : em.createNativeQuery("SELECT d.VisitCode, d.GenericDrugName, d.Dose, d.Frequency, d.Duration, " + + "d.DuartionUnit, d.LastModDate, d.DrugID, i.ItemFormID, d.DrugForm, d.QtyPrescribed, " + + "d.SpecialInstruction FROM db_iemr.t_prescribeddrug d " + + "LEFT JOIN db_iemr.m_item i ON i.ItemID = d.DrugID " + + "WHERE d.VisitCode IN (:codes) AND d.DrugID IS NOT NULL AND d.Deleted = false " + + "ORDER BY d.PrescribedDrugID") + .setParameter("codes", chunk).getResultList()) { + Object[] r = (Object[]) o; + Map m = byVisit.get(((Number) r[0]).longValue()); + ((StringJoiner) m.get("medication")).add(r[1] != null ? r[1].toString() : ""); + ((StringJoiner) m.get("dosage")).add(r[2] != null ? r[2].toString() : ""); + ((StringJoiner) m.get("frequency")).add(r[3] != null ? r[3].toString() : ""); + ((StringJoiner) m.get("duration")).add(r[4] == null ? "" : r[5] == null ? r[4].toString() : r[4] + " " + r[5]); + latest(m, r[6]); + + // Full prescription per drug, so a saved record can show form, quantity and instructions. + Map drug = new LinkedHashMap<>(); + drug.put("drugID", ((Number) r[7]).intValue()); + drug.put("drugName", r[1]); + drug.put("itemFormID", r[8] != null ? ((Number) r[8]).intValue() : null); + drug.put("drugForm", r[9]); + drug.put("dose", r[2]); + drug.put("frequency", r[3]); + drug.put("duration", r[4] != null ? toInt(r[4]) : null); + drug.put("durationUnit", r[5]); + drug.put("qtyPrescribed", r[10] != null ? ((Number) r[10]).intValue() : null); + drug.put("instructions", r[11]); + ((List>) m.get("drugs")).add(drug); + } + + // Latest prescription instruction per visit is the visit's note. + for (Object o : em.createNativeQuery("SELECT VisitCode, Instruction, LastModDate FROM db_iemr.t_prescription " + + "WHERE VisitCode IN (:codes) AND Deleted = false ORDER BY PrescriptionID") + .setParameter("codes", chunk).getResultList()) { + Object[] r = (Object[]) o; + Map m = byVisit.get(((Number) r[0]).longValue()); + if (r[1] != null && !r[1].toString().isBlank()) m.put("notes", r[1].toString()); + latest(m, r[2]); + } + } + + com.google.gson.Gson gson = new com.google.gson.Gson(); + List> records = new ArrayList<>(); + for (Map m : byVisit.values()) { + List complaints = (List) m.get("chiefComplaint"); + m.put("chiefComplaint", complaints.isEmpty() ? null : gson.toJson(complaints)); + for (String key : new String[] { "medication", "dosage", "frequency", "duration" }) { + String joined = m.get(key).toString(); + m.put(key, joined.replace(",", "").isBlank() ? null : joined); + } + records.add(m); + } + return records; + } + + // updateDate = latest change on the visit, so the app picks up a later save on the same day. + private void latest(Map m, Object modDate) { + if (modDate instanceof Date && (m.get("updateDate") == null || ((Date) modDate).after((Date) m.get("updateDate")))) { + m.put("updateDate", modDate); + } + } + + // Name, age (years), gender for the issue row shown on the Inventory dispense screen. Missing details + // never block dispensing, same as Inventory-API. + private Object[] getPatientDetails(Long beneficiaryRegID) { + try { + RMNCHMBeneficiarymapping mapping = beneficiaryRepo.getById(BigInteger.valueOf(beneficiaryRegID)); + RMNCHMBeneficiarydetail detail = (mapping != null && mapping.getBenDetailsId() != null) + ? beneficiaryRepo.getDetailsById(mapping.getBenDetailsId()) + : null; + if (detail == null) return new Object[3]; + StringJoiner name = new StringJoiner(" "); + for (String part : new String[] { detail.getFirstName(), detail.getMiddleName(), detail.getLastName() }) { + if (part != null && !part.isBlank()) name.add(part.trim()); + } + Integer age = detail.getDob() != null + ? Period.between(detail.getDob().toLocalDateTime().toLocalDate(), LocalDate.now()).getYears() + : null; + String fullName = name.length() > 150 ? name.toString().substring(0, 150) : name.toString(); + return new Object[] { fullName.isEmpty() ? null : fullName, age, detail.getGender() }; + } catch (Exception e) { + logger.warn("Cannot read patient details for benRegID " + beneficiaryRegID + ": " + e.getMessage()); + return new Object[3]; + } + } + + /** + * Dispensing off (stoptb.dispense.enabled=false): the prescription is saved as the app sent it. No store or + * stock check and no patient issue, so drugs from the app's own list need not exist in this camp's store; + * DrugID, name and form are taken from the request. A new save replaces the visit's earlier prescription + * (same as chief complaints), so an edit or retry does not leave a second copy. + */ + private Map prescribeWithoutDispensing(Map prescription, + List> drugs, Long beneficiaryRegID, BenVisitDetail visit, String createdBy, + Integer vanID, Integer parkingPlaceID) { + for (Map d : drugs) { + if (toInt(d.get("drugID")) == null) throw new IllegalArgumentException("drugID is required for every drug"); + } + // Processed='N' so the removal is uploaded to central again (central updates rows it already has). + for (String table : new String[] { "t_prescribeddrug", "t_prescription" }) { + em.createNativeQuery("UPDATE db_iemr." + table + " SET Deleted = true, Processed = 'N', ModifiedBy = :user " + + "WHERE BeneficiaryRegID = :ben AND VisitCode = :visitCode AND Deleted = false") + .setParameter("user", createdBy) + .setParameter("ben", beneficiaryRegID) + .setParameter("visitCode", visit.getVisitCode()) + .executeUpdate(); + } + + Map result = new LinkedHashMap<>(); + String instruction = toStr(prescription.get("instruction")); + if (!drugs.isEmpty() || (instruction != null && !instruction.isBlank())) { + long prescriptionID = insertPrescription(beneficiaryRegID, visit, instruction, createdBy, vanID, + parkingPlaceID); + for (Map d : drugs) { + Integer qty = toInt(d.get("qtyPrescribed")); + if (qty == null || qty <= 0) qty = calculateQuantity(d, toInt(d.get("itemFormID"))); // null if unknown + em.createNativeQuery("INSERT INTO db_iemr.t_prescribeddrug (BeneficiaryRegID, BenVisitID, " + + "ProviderServiceMapID, VisitCode, PrescriptionID, DrugForm, DrugID, GenericDrugName, Dose, " + + "Frequency, Duration, DuartionUnit, SpecialInstruction, QtyPrescribed, Deleted, Processed, " + + "CreatedBy, VanID, ParkingPlaceID) VALUES (:ben, :visitID, :psm, :visitCode, " + + ":prescriptionID, :form, :drugID, :name, :dose, :frequency, :duration, :durationUnit, " + + ":instruction, :qty, false, 'N', :user, :van, :pp)") + .setParameter("ben", beneficiaryRegID) + .setParameter("visitID", visit.getBenVisitId()) + .setParameter("psm", visit.getProviderServiceMapID()) + .setParameter("visitCode", visit.getVisitCode()) + .setParameter("prescriptionID", prescriptionID) + .setParameter("form", toStr(d.get("drugForm"))) + .setParameter("drugID", toInt(d.get("drugID"))) + .setParameter("name", toStr(d.get("drugName"))) + .setParameter("dose", toStr(d.get("dose"))) + .setParameter("frequency", toStr(d.get("frequency"))) + .setParameter("duration", toStr(d.get("duration"))) + .setParameter("durationUnit", toStr(d.get("durationUnit"))) + .setParameter("instruction", toStr(d.get("instructions"))) + .setParameter("qty", qty) + .setParameter("user", createdBy) + .setParameter("van", vanID) + .setParameter("pp", parkingPlaceID) + .executeUpdate(); + stampVanSerialNo("t_prescribeddrug", "PrescribedDrugID", lastInsertId()); + } + result.put("prescriptionID", prescriptionID); + } + result.put("dispensed", new ArrayList>()); + return result; + } + + // t_prescription row for the visit (Stop TB service line, same as the visit). + private long insertPrescription(Long beneficiaryRegID, BenVisitDetail visit, String instruction, String createdBy, + Integer vanID, Integer parkingPlaceID) { + em.createNativeQuery("INSERT INTO db_iemr.t_prescription (BeneficiaryRegID, BenVisitID, ProviderServiceMapID, VisitCode, " + + "Instruction, Deleted, Processed, CreatedBy, VanID, ParkingPlaceID) " + + "VALUES (:ben, :visitID, :psm, :visitCode, :instruction, false, 'N', :user, :van, :pp)") + .setParameter("ben", beneficiaryRegID) + .setParameter("visitID", visit.getBenVisitId()) + .setParameter("psm", visit.getProviderServiceMapID()) + .setParameter("visitCode", visit.getVisitCode()) + .setParameter("instruction", instruction) + .setParameter("user", createdBy) + .setParameter("van", vanID) + .setParameter("pp", parkingPlaceID) + .executeUpdate(); + long prescriptionID = lastInsertId(); + stampVanSerialNo("t_prescription", "PrescriptionID", prescriptionID); + return prescriptionID; + } + + /** + * Units to give when the app sends no quantity – MMU's rule (CommonNurseServiceImpl.calculateQtyPrescribed). + * Tablets/capsules: tablets per intake x intakes per day x days, rounded up. Tablets per intake come from the + * dose (Half Tab 0.5, One Tab 1, One & Half Tab 1.5, Two Tabs 2); no dose sent = one tablet (capsule always 1). + * Single/Stat dose = one intake. SOS = up to one intake a day (one intake when no duration). Once in a week = + * one intake per 7 days. Other forms (syrup, cream, injection, drops...): 1 bottle/tube/vial. Null when a + * tablet/capsule quantity cannot be worked out (unknown frequency or missing duration). + */ + private Integer calculateQuantity(Map drug, Integer formID) { + if (formID == null || (formID != 1 && formID != 2)) return 1; // 1 = Tablet, 2 = Capsule + double perIntake = formID == 2 ? 1 : tabletsPerIntake(toStr(drug.get("dose"))); + String frequency = Optional.ofNullable(toStr(drug.get("frequency"))).orElse("").toUpperCase(); + if (frequency.contains("SINGLE DOSE") || frequency.contains("STAT")) return (int) Math.ceil(perIntake); + + Integer duration = toInt(drug.get("duration")); + boolean sos = frequency.contains("SOS"); + if (duration == null || duration <= 0) return sos ? (int) Math.ceil(perIntake) : null; + String unit = Optional.ofNullable(toStr(drug.get("durationUnit"))).orElse("Day").toLowerCase(); + int days = unit.startsWith("week") ? duration * 7 : unit.startsWith("month") ? duration * 30 + : unit.startsWith("year") ? duration * 365 : duration; + + if (frequency.contains("WEEK")) return (int) Math.ceil(perIntake * ((days + 6) / 7)); // Once in a Week + int perDay; + if (frequency.contains("QID") || frequency.contains("FOUR")) perDay = 4; + else if (frequency.contains("TID") || frequency.contains("THRICE")) perDay = 3; + else if (frequency.contains("BD") || frequency.contains("TWICE")) perDay = 2; + else if (frequency.contains("OD") || frequency.contains("ONCE DAILY") || sos) perDay = 1; // SOS: up to 1 a day + else return null; // unknown frequency + return (int) Math.ceil(perIntake * perDay * days); + } + + // MMU dose names (m_drugdose); anything else or no dose = one tablet + private static double tabletsPerIntake(String dose) { + if (dose == null) return 1; + String d = dose.trim().toLowerCase(); + if (d.startsWith("half")) return 0.5; + if (d.startsWith("one & half") || d.startsWith("one and half") || d.startsWith("1.5")) return 1.5; + if (d.startsWith("two")) return 2; + return 1; + } + + // ItemID, ItemName, strength+UOM, form, route, isEDL, IssueType, ItemFormID — only if the drug is mapped to + // the store on its own service line (same join as v_drugforprescription, so the save accepts what the list shows). + private Object[] getStoreItem(Integer facilityID, Integer drugID) { + List rows = em.createNativeQuery("SELECT i.ItemID, i.ItemName, CONCAT_WS(' ', i.Strength, u.UOMName), " + + "f.ItemFormName, r.RouteName, i.isEDL, c.IssueType, i.ItemFormID " + + "FROM db_iemr.m_item i " + + "JOIN db_iemr.m_itemfacilitymapping m ON m.ItemID = i.ItemID AND m.FacilityID = :facilityID " + + "AND m.ProviderServiceMapID = i.ProviderServiceMapID AND m.Deleted = false " + + "LEFT JOIN db_iemr.m_uom u ON u.UOMID = i.UOMID " + + "LEFT JOIN db_iemr.m_itemform f ON f.ItemFormID = i.ItemFormID " + + "LEFT JOIN db_iemr.m_routeofadmin r ON r.RouteID = i.RouteID " + + "LEFT JOIN db_iemr.m_itemcategory c ON c.ItemCategoryID = i.ItemCategoryID " + + "WHERE i.ItemID = :drugID AND i.Deleted = false") + .setParameter("facilityID", facilityID) + .setParameter("drugID", drugID) + .getResultList(); + if (rows.isEmpty()) { + throw new IllegalArgumentException("drugID " + drugID + " is not available in this camp's store"); + } + return (Object[]) rows.get(0); + } + + // A batch is usable if it has no expiry date (counted by v_drugforprescription too) or lasts the course. + private static final String USABLE_BATCH = "AND (ExpiryDate IS NULL OR ExpiryDate > DATE_ADD(CURDATE(), INTERVAL :days DAY)) "; + + // Usable batches (VanSerialNo, QuantityInHand), locked for this transaction, in the category's issue order. + @SuppressWarnings("unchecked") + private List lockBatches(Integer facilityID, Integer drugID, String issueType, int durationDays) { + String order; + if ("Last In First Out".equalsIgnoreCase(issueType)) order = "CreatedDate DESC, ItemStockEntryID DESC"; + else if ("First In First Out".equalsIgnoreCase(issueType)) order = "CreatedDate ASC, ItemStockEntryID ASC"; + else order = "ExpiryDate IS NULL, ExpiryDate ASC, ItemStockEntryID ASC"; // First Expiry First Out (default) + Query q = em.createNativeQuery("SELECT VanSerialNo, QuantityInHand FROM db_iemr.t_itemstockentry " + + "WHERE FacilityID = :facilityID AND ItemID = :drugID AND Deleted = false AND QuantityInHand > 0 " + + USABLE_BATCH + "ORDER BY " + order + " FOR UPDATE") + .setParameter("facilityID", facilityID) + .setParameter("drugID", drugID) + .setParameter("days", durationDays); + return (List) q.getResultList(); + } + + private int availableQuantity(Integer facilityID, Integer drugID, int durationDays) { + Object v = em.createNativeQuery("SELECT COALESCE(SUM(QuantityInHand), 0) FROM db_iemr.t_itemstockentry " + + "WHERE FacilityID = :facilityID AND ItemID = :drugID AND Deleted = false AND QuantityInHand > 0 " + + USABLE_BATCH) + .setParameter("facilityID", facilityID) + .setParameter("drugID", drugID) + .setParameter("days", durationDays) + .getSingleResult(); + return ((Number) v).intValue(); + } + + private Integer getStorePsm(Integer facilityID) { + Object v = em.createNativeQuery("SELECT ProviderServiceMapID FROM db_iemr.m_facility WHERE FacilityID = :facilityID") + .setParameter("facilityID", facilityID) + .getSingleResult(); + return v != null ? ((Number) v).intValue() : null; + } + + // Longest treatment among the lines for this drug, so the batch lasts until the course ends. + private int maxDurationDays(List> drugs, Integer drugID) { + int max = 0; + for (Map d : drugs) { + if (!drugID.equals(toInt(d.get("drugID")))) continue; + Integer n = toInt(d.get("duration")); + if (n == null || n <= 0) continue; + String unit = Optional.ofNullable(toStr(d.get("durationUnit"))).orElse("Day").toLowerCase(); + int days = unit.startsWith("week") ? n * 7 : unit.startsWith("month") ? n * 30 : unit.startsWith("year") ? n * 365 : n; + max = Math.max(max, days); + } + return max; + } + + private long lastInsertId() { + return ((Number) em.createNativeQuery("SELECT LAST_INSERT_ID()").getSingleResult()).longValue(); + } + + // VanSerialNo = own ID, as every MMU/Inventory save does, so van-to-server sync can identify the row. + private void stampVanSerialNo(String table, String idColumn, long id) { + em.createNativeQuery("UPDATE db_iemr." + table + " SET VanSerialNo = " + idColumn + " WHERE " + idColumn + " = :id") + .setParameter("id", id) + .executeUpdate(); + } + + @SuppressWarnings("unchecked") + private List> toList(Object v) { + if (v instanceof List) return (List>) v; + return Collections.emptyList(); + } + + private Integer toInt(Object v) { + if (v == null) return null; + if (v instanceof Number) return ((Number) v).intValue(); + String s = v.toString().trim(); + if (s.isEmpty()) return null; + try { + return (int) Double.parseDouble(s); + } catch (NumberFormatException e) { + return null; + } + } + + private String toStr(Object v) { + return v != null ? v.toString() : null; + } +} diff --git a/src/main/java/com/iemr/flw/service/StopTBService.java b/src/main/java/com/iemr/flw/service/StopTBService.java index e549daa6f..4011d4315 100644 --- a/src/main/java/com/iemr/flw/service/StopTBService.java +++ b/src/main/java/com/iemr/flw/service/StopTBService.java @@ -1,5 +1,6 @@ package com.iemr.flw.service; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -19,4 +20,15 @@ public interface StopTBService { // Nurse — Diagnostics List> saveDiagnostics(List> dataList) throws Exception; Map getAllDiagnostics(Integer providerServiceMapID, Integer villageID) throws Exception; + + // The same records the getAll endpoints return, for specific rows (STOP-TB changes feed), keyed by + // the row id. Deleted rows, and rows getAll would not return, are left out. + Map> getNurseTBScreeningsByIds(Collection ids); + Map> getGeneralExaminationsByIds(Collection ids); + Map> getDiagnosticsByIds(Collection ids); + // Standard-table OPD visits, keyed by BenVisitID + Map> getGeneralOpdByVisitIds(Collection benVisitIds); + // Older free-text OPD rows (tb_stoptb_general_opd), keyed by their id; left out when the same visit + // also exists in the standard tables, as getAllGeneralOpd does + Map> getLegacyGeneralOpdByIds(Collection ids); } diff --git a/src/main/java/com/iemr/flw/service/StopTBSyncService.java b/src/main/java/com/iemr/flw/service/StopTBSyncService.java new file mode 100644 index 000000000..9e8c94b6a --- /dev/null +++ b/src/main/java/com/iemr/flw/service/StopTBSyncService.java @@ -0,0 +1,19 @@ +package com.iemr.flw.service; + +import java.util.List; +import java.util.Map; + +public interface StopTBSyncService { + + /** + * One page of the STOP-TB changes feed for the given villages: a first full download when the + * token is missing, from another hub or too old; otherwise only what changed after it. + * Returns {supported:false} where the change log isn't installed, so the app keeps its full pull. + * The service and villages must be mapped to userId (from the JWT); empty villageIds means all of + * the user's villages for that service. + * types limits the feed to the record types the app applies (StopTBSyncType api names); null or + * empty means all. The app must start a new snapshot (no token) whenever it changes this list. + */ + Map getChanges(Integer userId, String token, Integer providerServiceMapID, List villageIds, + List types, Integer limit, String authorisation); +} diff --git a/src/main/java/com/iemr/flw/service/SupervisorDashboardService.java b/src/main/java/com/iemr/flw/service/SupervisorDashboardService.java index bc580a26d..d406bf628 100644 --- a/src/main/java/com/iemr/flw/service/SupervisorDashboardService.java +++ b/src/main/java/com/iemr/flw/service/SupervisorDashboardService.java @@ -5,7 +5,8 @@ public interface SupervisorDashboardService { - String getSupervisorDashboard(Integer supervisorUserID, Integer month, Integer year); + String getSupervisorDashboard(Integer supervisorUserID, Integer month, Integer year,Integer facilityId); + String getSupervisorSubCenter(Integer supervisorUserID, Integer month, Integer year); Map getAshasAtFacility(Integer supervisorId, Integer facilityId, Integer month, Integer year, Integer approvalStatusID); diff --git a/src/main/java/com/iemr/flw/service/TBConfirmedCaseService.java b/src/main/java/com/iemr/flw/service/TBConfirmedCaseService.java index 6471f7996..67ca9e1a6 100644 --- a/src/main/java/com/iemr/flw/service/TBConfirmedCaseService.java +++ b/src/main/java/com/iemr/flw/service/TBConfirmedCaseService.java @@ -1,6 +1,8 @@ package com.iemr.flw.service; import com.iemr.flw.domain.iemr.TBConfirmedCaseDTO; +import com.iemr.flw.dto.iemr.TbReferralFollowUpDTO; +import com.iemr.flw.dto.iemr.TbTptFollowUpDTO; import org.springframework.stereotype.Service; import java.util.List; @@ -15,4 +17,15 @@ public interface TBConfirmedCaseService { String getByUserId(String authorisation) throws Exception; String getByProviderServiceMapId(Integer providerServiceMapID, Integer villageID) throws Exception; + + // One record per row, in the same JSON shape getAll returns (STOP-TB changes feed), keyed by id. + java.util.Map getRecordsByIds(java.util.Collection ids); + + String saveReferralFollowUp(TbReferralFollowUpDTO requestDTO, String token); + + String getReferralFollowUp(String token); + + String saveTptFollowUp(TbTptFollowUpDTO requestDTO, String token); + + String getTptFollowUp(String token); } diff --git a/src/main/java/com/iemr/flw/service/TBSuspectedService.java b/src/main/java/com/iemr/flw/service/TBSuspectedService.java index 32d882a80..97d9e0d87 100644 --- a/src/main/java/com/iemr/flw/service/TBSuspectedService.java +++ b/src/main/java/com/iemr/flw/service/TBSuspectedService.java @@ -13,4 +13,7 @@ public interface TBSuspectedService { String save(TBSuspectedRequestDTO tbScreeningList) throws Exception; String getByUserId(GetBenRequestHandler userId); + + // One record per row, in the same JSON shape getAll returns (STOP-TB changes feed), keyed by id. + java.util.Map getRecordsByIds(java.util.Collection ids); } diff --git a/src/main/java/com/iemr/flw/service/impl/AdolescentHealthServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/AdolescentHealthServiceImpl.java index 8be992afe..9be73f7c5 100644 --- a/src/main/java/com/iemr/flw/service/impl/AdolescentHealthServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/AdolescentHealthServiceImpl.java @@ -48,9 +48,11 @@ public String saveAll(AdolescentHealthDTO adolescentHealthDTO) { if (existingRecord.isPresent()) { // If record exists, update the existing one + adolescentHealth.setUserId(adolescentHealthDTO.getUserId()); updateAdolescentHealth(existingRecord, adolescentHealth); } else { // If the record does not exist, create a new one + adolescentHealth.setUserId(adolescentHealthDTO.getUserId()); adolescentHealthRepo.save(adolescentHealth); checkAndAddIncentives(adolescentHealth); } diff --git a/src/main/java/com/iemr/flw/service/impl/BeneficiaryServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/BeneficiaryServiceImpl.java index 7136f4905..bc1edc372 100644 --- a/src/main/java/com/iemr/flw/service/impl/BeneficiaryServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/BeneficiaryServiceImpl.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -119,6 +120,9 @@ public class BeneficiaryServiceImpl implements BeneficiaryService { @Autowired private BenPhysicalVitalRepo benPhysicalVitalRepo; + @Autowired + private WorklistPagePrefetch worklistPagePrefetch; + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd-MM-yyyy"); @@ -141,8 +145,10 @@ public String getBenData(GetBenRequestHandler request, String authorisation) thr // O(village size), on every page including page 0. See BeneficiaryRepo for the // underlying query. if (request.getProviderServiceMapID() != null && request.getVillageID() != null) { + long tStart = System.currentTimeMillis(); long totalCount = beneficiaryRepo.countVillageWorklist( request.getProviderServiceMapID(), request.getVillageID()); + long tCount = System.currentTimeMillis(); if (totalCount == 0) return null; @@ -152,6 +158,7 @@ public String getBenData(GetBenRequestHandler request, String authorisation) thr List addressIds = beneficiaryRepo.getVillageWorklistAddressIds( request.getProviderServiceMapID(), request.getVillageID(), pageSize, offset); + long tIds = System.currentTimeMillis(); if (addressIds.isEmpty()) return null; @@ -159,17 +166,28 @@ public String getBenData(GetBenRequestHandler request, String authorisation) thr // Hibernate 6.4.1 bug there: "Binding is multi-valued; illegal call to // #getBindValue" even with a well-formed, non-empty id list (confirmed live // against 192.168.45.40 - the 10 rows returned have no NULLs/type issues, so - // this isn't a data problem). Looping getAddressById() per id sidesteps - // Hibernate's collection-parameter binding entirely - only ever pageSize (10) - // calls per page, same order-preservation as before. - List pageAddresses = addressIds.stream() - .map(beneficiaryRepo::getAddressById) - .filter(java.util.Objects::nonNull) - .collect(Collectors.toList()); + // this isn't a data problem). WorklistPagePrefetch reads them in one native query + // with the ids as numeric literals (no collection binding), keeping page order; + // the per-id loop stays as the fallback. + List pageAddresses; + try { + pageAddresses = worklistPagePrefetch.addressesInOrder(addressIds); + } catch (Exception e) { + logger.warn("Worklist address batch failed, reading one by one: " + e.getMessage()); + pageAddresses = addressIds.stream() + .map(beneficiaryRepo::getAddressById) + .filter(java.util.Objects::nonNull) + .collect(Collectors.toList()); + } if (pageAddresses.isEmpty()) return null; + long tAddr = System.currentTimeMillis(); - return getMappingsForAddressIDs(pageAddresses, totalPage, authorisation); + String timingLabel = "[WORKLIST-TIMING] psm=" + request.getProviderServiceMapID() + + " village=" + request.getVillageID() + " page=" + request.getPageNo() + "/" + totalPage + + " pageSize=" + pageSize + " villageTotal=" + totalCount + + " | countMs=" + (tCount - tStart) + " idsMs=" + (tIds - tCount) + " addressMs=" + (tAddr - tIds); + return getMappingsForAddressIDs(pageAddresses, totalPage, authorisation, timingLabel, tStart); } // Normal FLW/ASHA path @@ -213,13 +231,59 @@ public String getBenData(GetBenRequestHandler request, String authorisation) thr return getMappingsForAddressIDs( pageResult.getContent(), pageResult.getTotalPages(), - authorisation + authorisation, + null, + 0L ); } + // timingLabel != null logs one [WORKLIST-TIMING] line per page (Stop TB village path) so camp-laptop + // logs show where each page's time goes. private String getMappingsForAddressIDs(List addressList, int totalPage, - String authorisation) { + String authorisation, String timingLabel, long tStart) { + long tBuildStart = System.currentTimeMillis(); + List> resultList = buildBeneficiaryRecords(addressList, authorisation); + long tBuild = System.currentTimeMillis(); + + Map response = new HashMap<>(); + response.put("data", resultList); + response.put("pageSize", Integer.parseInt(door_to_door_page_size)); + response.put("totalPage", totalPage); + Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); + String json = gson.toJson(response); + + if (timingLabel != null) { + long tEnd = System.currentTimeMillis(); + logger.info(timingLabel + " buildMs=" + (tBuild - tBuildStart) + " jsonMs=" + (tEnd - tBuild) + + " totalMs=" + (tEnd - tStart) + " rows=" + resultList.size() + "/" + addressList.size() + + " bytes=" + json.length()); + } + return json; + } + + // One record per beneficiary, in the same JSON shape getBeneficiaryData returns, keyed by BenRegId. + // Used by the STOP-TB changes feed; a BenRegId with no mapping or address is left out. + @Override + public Map getBeneficiaryRecordsByRegIds(List benRegIds, String authorisation) { + List addresses = new ArrayList<>(); + for (Long benRegId : benRegIds) { + RMNCHMBeneficiarymapping m = beneficiaryRepo.getById(BigInteger.valueOf(benRegId)); + if (m == null || m.getBenAddressId() == null) continue; + RMNCHMBeneficiaryaddress a = beneficiaryRepo.getAddressById(m.getBenAddressId()); + if (a != null) addresses.add(a); + } + Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); + Map records = new LinkedHashMap<>(); + for (Map record : buildBeneficiaryRecords(addresses, authorisation)) { + Object benRegId = record.get("BenRegId"); + if (benRegId != null) records.put(new BigInteger(benRegId.toString()).longValue(), gson.toJson(record)); + } + return records; + } + + private List> buildBeneficiaryRecords(List addressList, + String authorisation) { RMNCHHouseHoldDetails benHouseHoldRMNCH_ROBJ; RMNCHBeneficiaryDetailsRmnch benDetailsRMNCH_OBJ; RMNCHBornBirthDetails benBotnBirthRMNCH_ROBJ; @@ -233,10 +297,20 @@ private String getMappingsForAddressIDs(List addressLi Map resultMap; ArrayList> resultList = new ArrayList<>(); + // One query per table for the whole page; if it fails, every lookup below falls back to its + // original per-beneficiary query. + WorklistPagePrefetch.Page pf = null; + try { + pf = worklistPagePrefetch.load(addressList); + } catch (Exception e) { + logger.warn("Worklist page prefetch failed, using per-beneficiary queries: " + e.getMessage()); + } + for (RMNCHMBeneficiaryaddress a : addressList) { // exception by-passing try { - List mappingsForAddress = beneficiaryRepo.getByAddressID(a.getId()); + List mappingsForAddress = pf != null ? pf.mappings(a.getId()) + : beneficiaryRepo.getByAddressID(a.getId()); if(!mappingsForAddress.isEmpty()){ RMNCHMBeneficiarymapping m = mappingsForAddress.get(0); if (m != null) { @@ -249,38 +323,53 @@ private String getMappingsForAddressIDs(List addressLi benImageOBJ = new RMNCHMBeneficiaryImage(); benAddressOBJ = new RMNCHMBeneficiaryaddress(); benContactOBJ = new RMNCHMBeneficiarycontact(); - Map healthDetails = getBenHealthDetails(m.getBenRegId()); + Long regIdLong = m.getBenRegId() != null ? m.getBenRegId().longValue() : null; + // no m_benhealthidmapping row -> the original lookup returns an empty map too + Map healthDetails = (pf != null && regIdLong != null && !pf.hasHealthId(regIdLong)) + ? new HashMap<>() : getBenHealthDetails(m.getBenRegId()); if (m.getBenDetailsId() != null) { - benDetailsOBJ = beneficiaryRepo.getDetailsById(m.getBenDetailsId()); + benDetailsOBJ = pf != null ? pf.detail(m.getBenDetailsId()) + : beneficiaryRepo.getDetailsById(m.getBenDetailsId()); } if (m.getBenAccountID() != null) { - benAccountOBJ = beneficiaryRepo.getAccountById(m.getBenAccountID()); + benAccountOBJ = pf != null ? pf.account(m.getBenAccountID()) + : beneficiaryRepo.getAccountById(m.getBenAccountID()); } if (m.getBenImageId() != null) { - benImageOBJ = beneficiaryRepo.getImageById(m.getBenImageId().longValue()); + benImageOBJ = pf != null ? pf.image(m.getBenImageId()) + : beneficiaryRepo.getImageById(m.getBenImageId().longValue()); } if (m.getBenAddressId() != null) { - benAddressOBJ = beneficiaryRepo.getAddressById(m.getBenAddressId()); + benAddressOBJ = pf != null ? pf.address(m.getBenAddressId()) + : beneficiaryRepo.getAddressById(m.getBenAddressId()); } if (m.getBenContactsId() != null) { - benContactOBJ = beneficiaryRepo.getContactById(m.getBenContactsId()); + benContactOBJ = pf != null ? pf.contact(m.getBenContactsId()) + : beneficiaryRepo.getContactById(m.getBenContactsId()); } BigInteger benID = null; - if (m.getBenRegId() != null) - benID = beneficiaryRepo.getBenIdFromRegID(m.getBenRegId().longValue()); + if (regIdLong != null) { + // more than one row: let the original single-result query decide, as before + benID = (pf != null && pf.benIds(regIdLong).size() <= 1) + ? pf.benIds(regIdLong).stream().findFirst().orElse(null) + : beneficiaryRepo.getBenIdFromRegID(regIdLong); + } - if (m.getBenRegId() != null) { - List detailsByRegID = beneficiaryRepo - .getDetailsByRegID((m.getBenRegId()).longValue()); + if (regIdLong != null) { + List detailsByRegID = pf != null ? pf.rmnch(regIdLong) + : beneficiaryRepo.getDetailsByRegID(regIdLong); if(!detailsByRegID.isEmpty()){ benDetailsRMNCH_OBJ = detailsByRegID.get(0); } - benBotnBirthRMNCH_ROBJ = beneficiaryRepo.getBornBirthByRegID((m.getBenRegId()).longValue()); + benBotnBirthRMNCH_ROBJ = (pf != null && pf.bornBirth(regIdLong).size() <= 1) + ? pf.bornBirth(regIdLong).stream().findFirst().orElse(null) + : beneficiaryRepo.getBornBirthByRegID(regIdLong); if (benDetailsRMNCH_OBJ != null && benDetailsRMNCH_OBJ.getHouseoldId() != null) { - List householdsById = houseHoldRepo - .getByHouseHoldID(benDetailsRMNCH_OBJ.getHouseoldId()); + List householdsById = pf != null + ? pf.households(benDetailsRMNCH_OBJ.getHouseoldId()) + : houseHoldRepo.getByHouseHoldID(benDetailsRMNCH_OBJ.getHouseoldId()); if(!householdsById.isEmpty()){ benHouseHoldRMNCH_ROBJ = householdsById.get(0); } @@ -392,7 +481,8 @@ private String getMappingsForAddressIDs(List addressLi } else { // i_beneficiarydetails.dob is null for Stop TB mobile registrations // (Identity-API mapper commented out) — fall back to i_ben_flow_outreach.ben_dob - List flows = benFlowStatusRepo.findByBeneficiaryRegID(m.getBenRegId().longValue()); + List flows = pf != null ? pf.flows(regIdLong) + : benFlowStatusRepo.findByBeneficiaryRegID(m.getBenRegId().longValue()); if (!flows.isEmpty() && flows.get(0).getDob() != null) benDetailsRMNCH_OBJ.setDob(flows.get(0).getDob()); } @@ -535,10 +625,10 @@ private String getMappingsForAddressIDs(List addressLi // Identity-API started writing these tables). Long benRegIdLong = m.getBenRegId() != null ? m.getBenRegId().longValue() : null; if (benRegIdLong != null) { - List anthroList = - benAnthropometryRepo.findByBeneficiaryRegIDOrderByCreatedDateDesc(benRegIdLong); - List vitalList = - benPhysicalVitalRepo.findByBeneficiaryRegIDOrderByCreatedDateDesc(benRegIdLong); + List anthroList = pf != null ? pf.anthropometry(benRegIdLong) + : benAnthropometryRepo.findByBeneficiaryRegIDOrderByCreatedDateDesc(benRegIdLong); + List vitalList = pf != null ? pf.vitals(benRegIdLong) + : benPhysicalVitalRepo.findByBeneficiaryRegIDOrderByCreatedDateDesc(benRegIdLong); Map anthropometry = new HashMap<>(); for (BenAnthropometryDetail anthro : anthroList) { @@ -594,13 +684,24 @@ private String getMappingsForAddressIDs(List addressLi // adding asha id / created by - user id if (benAddressOBJ.getCreatedBy() != null) { - Integer userID = beneficiaryRepo.getUserIDByUserName(benAddressOBJ.getCreatedBy()); + String createdBy = benAddressOBJ.getCreatedBy(); + Integer userID; + if (pf != null && pf.userIdCache().containsKey(createdBy)) { + userID = pf.userIdCache().get(createdBy); + } else { + userID = beneficiaryRepo.getUserIDByUserName(createdBy); + if (pf != null) pf.userIdCache().put(createdBy, userID); + } if (userID != null && userID > 0) resultMap.put("ashaId", userID); } - // get HealthID of ben - if (m.getBenRegId() != null) { - fetchHealthIdByBenRegID(m.getBenRegId().longValue(), authorisation, resultMap); + // get HealthID of ben: read locally (same table FHIR-API reads), only when a mapping exists + if (regIdLong != null) { + if (pf != null) { + if (pf.hasHealthId(regIdLong)) putLocalHealthId(regIdLong, resultMap); + } else { + fetchHealthIdByBenRegID(regIdLong, authorisation, resultMap); + } } resultList.add(resultMap); @@ -616,13 +717,7 @@ private String getMappingsForAddressIDs(List addressLi logger.error("error for addressID :" + e.getMessage() + a.getId() + " and vanID : " + a.getVanID()); } } - - Map response = new HashMap<>(); - response.put("data", resultList); - response.put("pageSize", Integer.parseInt(door_to_door_page_size)); - response.put("totalPage", totalPage); - Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); - return gson.toJson(response); + return resultList; } @@ -670,6 +765,22 @@ private Map getBenBenVisitDetails(BigInteger benRegId) { return healthDetails; } + // Mirrors FHIR-API HealthIDServiceImpl.getBenHealthID: every m_benhealthidmapping row, with healthId + // replaced by t_healthid.HealthId when that is filled; the last row wins, as in fetchHealthIdByBenRegID. + private void putLocalHealthId(Long benRegID, Map resultMap) { + for (Object[] row : beneficiaryRepo.getHealthIdMappingRows(benRegID)) { + String healthId = row[0] != null ? row[0].toString() : null; + String healthIdNumber = row[1] != null ? row[1].toString() : null; + if (healthIdNumber != null) { + List fromHealthIdTable = beneficiaryRepo.getHealthIdByNumber(healthIdNumber); + if (!fromHealthIdTable.isEmpty() && fromHealthIdTable.get(0) != null && !fromHealthIdTable.get(0).isEmpty()) + healthId = fromHealthIdTable.get(0); + } + if (healthId != null) resultMap.put("healthId", healthId); + if (healthIdNumber != null) resultMap.put("healthIdNumber", healthIdNumber); + } + } + public void fetchHealthIdByBenRegID(Long benRegID, String authorization, Map resultMap) { Map requestMap = new HashMap(); requestMap.put("beneficiaryRegID", benRegID); diff --git a/src/main/java/com/iemr/flw/service/impl/CampaignServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/CampaignServiceImpl.java index f9c076327..ffacf0a35 100644 --- a/src/main/java/com/iemr/flw/service/impl/CampaignServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/CampaignServiceImpl.java @@ -459,7 +459,7 @@ private void checkMonthlyPulsePolioIncentive(Integer ashaId,LocalDate startDate, private void checkMonthlyFilariasisIncentive(Integer ashaId,LocalDate startDate,LocalDate endDate) { IncentiveActivity FILARIASIS_MEDICINE_DISTRIBUTION = incentivesRepo.findIncentiveMasterByNameAndGroup("FILARIASIS_MEDICINE_DISTRIBUTION", GroupName.ACTIVITY.getDisplayName()); if (FILARIASIS_MEDICINE_DISTRIBUTION != null) { - addAshaIncentiveRecord(FILARIASIS_MEDICINE_DISTRIBUTION, ashaId,startDate,endDate); + addAshaIncentiveRecordMda(FILARIASIS_MEDICINE_DISTRIBUTION, ashaId,startDate,endDate); } } @@ -473,7 +473,6 @@ private void checkMonthlyPulseOrsDistribution(Integer ashaId,LocalDate startDate } private void addAshaIncentiveRecord(IncentiveActivity incentiveActivity, Integer ashaId,LocalDate startDate,LocalDate endDate) { - Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now()); Timestamp startOfMonth = Timestamp.valueOf(startDate.atStartOfDay()); Timestamp endOfMonth = Timestamp.valueOf(endDate.atStartOfDay()); @@ -490,11 +489,41 @@ private void addAshaIncentiveRecord(IncentiveActivity incentiveActivity, Integer if (record == null) { record = new IncentiveActivityRecord(); record.setActivityId(incentiveActivity.getId()); - record.setCreatedDate(timestamp); + record.setCreatedDate(endOfMonth); record.setCreatedBy(userServiceRoleRepo.getUserNamedByUserId(ashaId)); - record.setStartDate(timestamp); - record.setEndDate(timestamp); - record.setUpdatedDate(timestamp); + record.setStartDate(endOfMonth); + record.setEndDate(endOfMonth); + record.setUpdatedDate(endOfMonth); + record.setUpdatedBy(userServiceRoleRepo.getUserNamedByUserId(ashaId)); + record.setBenId(0L); + record.setAshaId(ashaId); + record.setAmount(Long.valueOf(incentiveActivity.getRate())); + recordRepo.save(record); + } + } + + + private void addAshaIncentiveRecordMda(IncentiveActivity incentiveActivity, Integer ashaId,LocalDate startDate,LocalDate endDate) { + + Timestamp startOfMonth = Timestamp.valueOf(startDate.atStartOfDay()); + Timestamp endOfMonth = Timestamp.valueOf(endDate.atStartOfDay()); + + IncentiveActivityRecord record = recordRepo.findRecordByActivityIdCreatedDateBenId( + incentiveActivity.getId(), + endOfMonth, + 0L,ashaId + + ); + + + if (record == null) { + record = new IncentiveActivityRecord(); + record.setActivityId(incentiveActivity.getId()); + record.setCreatedDate(endOfMonth); + record.setCreatedBy(userServiceRoleRepo.getUserNamedByUserId(ashaId)); + record.setStartDate(endOfMonth); + record.setEndDate(endOfMonth); + record.setUpdatedDate(endOfMonth); record.setUpdatedBy(userServiceRoleRepo.getUserNamedByUserId(ashaId)); record.setBenId(0L); record.setAshaId(ashaId); diff --git a/src/main/java/com/iemr/flw/service/impl/ChildCareServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/ChildCareServiceImpl.java index e65e0f53e..adf47563a 100644 --- a/src/main/java/com/iemr/flw/service/impl/ChildCareServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/ChildCareServiceImpl.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; +import com.iemr.flw.domain.identity.RMNCHBeneficiaryDetailsRmnch; import com.iemr.flw.domain.iemr.*; import com.iemr.flw.dto.identity.GetBenRequestHandler; import com.iemr.flw.dto.iemr.*; @@ -16,6 +17,7 @@ import com.iemr.flw.service.UserService; import com.iemr.flw.utils.JwtUtil; import jakarta.persistence.EntityNotFoundException; +import jakarta.transaction.Transactional; import org.aspectj.weaver.ast.Or; import org.modelmapper.ModelMapper; import org.slf4j.Logger; @@ -36,6 +38,7 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @Service @@ -98,6 +101,7 @@ public class ChildCareServiceImpl implements ChildCareService { @Autowired private PNCVisitRepo pncVisitRepo; + private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>(); @Override public String registerHBYC(List hbycDTOs) { @@ -198,7 +202,7 @@ public List getHbycRecords(GetBenRequestHandler dto) { addIfValid(fields, "is_child_sick", convert(hbycChildVisit.getIs_child_sick())); addIfValid(fields, "exclusive_breastfeeding", convert(hbycChildVisit.getExclusive_breastfeeding())); addIfValid(fields, "mother_counseled_ebf", convert(hbycChildVisit.getMother_counseled_ebf())); -// addIfValid(fields, "complementary_feeding", convert(hbycChildVisit.getComplementary_feeding())); + addIfValid(fields, "complementary_feeding", convert(hbycChildVisit.getComplementary_feeding())); addIfValid(fields, "mother_counseled_cf", convert(hbycChildVisit.getMother_counseled_cf())); addIfValid(fields, "weight_recorded", convert(hbycChildVisit.getWeight_recorded())); addIfValid(fields, "developmental_delay", convert(hbycChildVisit.getDevelopmental_delay())); @@ -212,7 +216,7 @@ public List getHbycRecords(GetBenRequestHandler dto) { addIfValid(fields, "parenting_counseling", convert(hbycChildVisit.getParenting_counseling())); addIfValid(fields, "family_planning_counseling", convert(hbycChildVisit.getFamily_planning_counseling())); addIfValid(fields, "diarrhoea_episode", convert(hbycChildVisit.getDiarrhoea_episode())); -// addIfValid(fields, "breathing_difficulty", convert(hbycChildVisit.getBreathing_difficulty())); + addIfValid(fields, "breathing_difficulty", convert(hbycChildVisit.getBreathing_difficulty())); addIfValid(fields, "temperature_check", hbycChildVisit.getTemperature()); addIfValid(fields, "mcp_card_images", hbycChildVisit.getMcp_card_images()); @@ -279,12 +283,14 @@ public List getHBNCDetails(GetBenRequestHandler dto) { addIfValid(fields, "umbilical_stump", visit.getUmbilical_stump()); addIfValid(fields, "discharged_from_sncu", convert(visit.getDischarged_from_sncu())); addIfValid(fields, "discharge_summary_upload", visit.getDischarge_summary_upload()); + addIfValid(fields, "is_admitted_in_sncu", convert(visit.getIs_admitted_in_sncu())); // Add more fields as required responseDTO.setFields(fields); result.add(responseDTO); + } page++; @@ -371,28 +377,94 @@ public String saveHBNCDetails(List hbncRequestDTOs, Integer user return null; } + @Override + public String saveHBNCDetails(List hbncRequestDTOs) { + try { + List hbncList = new ArrayList<>(); + + hbncRequestDTOs.forEach(it -> { + if (it.getVisitDate() != null) { + + HbncVisitDTO hbncVisitDTO = it.getFields(); + hbncVisitDTO.setVisit_date(it.getVisitDate()); + HbncVisit hbncVisit = hbncVisitRepo.findByBeneficiaryIdAndVisit_day(it.getBeneficiaryId(), hbncVisitDTO.getVisit_day()); + + if (hbncVisit != null) { + Long id = hbncVisit.getId(); + modelMapper.map(hbncVisitDTO, hbncVisit); + hbncVisit.setId(id); + } else { + hbncVisit = new HbncVisit(); + modelMapper.map(hbncVisitDTO, hbncVisit); + hbncVisit.setBeneficiaryId(it.getBeneficiaryId()); + hbncVisit.setAshaId(userRepo.getUserIdByName(it.getUserName())); + hbncVisit.setCreatedBy(it.getUserName()); + hbncVisit.setHouseHoldId(it.getHouseHoldId()); + hbncVisit.setId(null); + } + hbncList.add(hbncVisit); + } + }); + + + hbncVisitRepo.saveAll(hbncList); + + + logger.info("HBNC details saved"); + return "no of hbnc details saved: " + (hbncList.size()); + } catch (Exception e) { + logger.info("Saving HBNC details failed with error : " + e.getMessage()); + } + return null; + } + @Override public List getChildVaccinationDetails(GetBenRequestHandler dto) { try { String user = beneficiaryRepo.getUserName(dto.getAshaId()); - List vaccinationDetails = childVaccinationRepo.getChildVaccinationDetails(user, dto.getFromDate(), dto.getToDate()); + List vaccinationDetails = + childVaccinationRepo.getChildVaccinationDetails(user); - List result = new ArrayList<>(); - vaccinationDetails.forEach(childVaccination -> { - ChildVaccinationDTO vaccinationDTO = mapper.convertValue(childVaccination, ChildVaccinationDTO.class); - if(childVaccination.getBeneficiaryRegId()!=null){ - BigInteger benId = beneficiaryRepo.getBenIdFromRegID(childVaccination.getBeneficiaryRegId()); - vaccinationDTO.setBeneficiaryId(benId.longValue()); + if (vaccinationDetails.isEmpty()) { + return new ArrayList<>(); + } - } - if(!vaccinationDetails.isEmpty()){ - checkAndAddIncentives(vaccinationDetails); + // Bulk fetch all beneficiary IDs in ONE query instead of one-per-record + List regIds = vaccinationDetails.stream() + .map(ChildVaccination::getBeneficiaryRegId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + Map regIdToBenIdMap = new HashMap<>(); + if (!regIds.isEmpty()) { + List benIdRows = beneficiaryRepo.getBenIdsFromRegIds(regIds); + for (Object[] row : benIdRows) { + Long regId = ((Number) row[0]).longValue(); + BigInteger benId = row[1] == null + ? null + : new BigInteger(row[1].toString()); + regIdToBenIdMap.put(regId, benId); } + } + // Call ONCE for the whole batch — not per record + checkAndAddIncentives(vaccinationDetails); + + List result = new ArrayList<>(); + for (ChildVaccination childVaccination : vaccinationDetails) { + ChildVaccinationDTO vaccinationDTO = mapper.convertValue(childVaccination, ChildVaccinationDTO.class); + + if (childVaccination.getBeneficiaryRegId() != null) { + BigInteger benId = regIdToBenIdMap.get(childVaccination.getBeneficiaryRegId()); + if (benId != null) { + vaccinationDTO.setBeneficiaryId(benId.longValue()); + } + } result.add(vaccinationDTO); - }); + } + return result; } catch (Exception e) { logger.error(e.getMessage()); @@ -516,6 +588,7 @@ public String saveSamDetails(List samRequest, Integer userId, String use samVisit.setVisitLabel(samDTO.getFields().getVisit_label()); samVisit.setWeightForHeightStatus(samDTO.getFields().getWeight_for_height_status()); samVisit.setIsChildReferredNrc(samDTO.getFields().getIs_child_referred_nrc()); + samVisit.setIsChildAdmittedNrc(samDTO.getFields().getIs_child_admitted_nrc()); samVisit.setNrcAdmissionDate(samDTO.getFields().getNrc_admission_date()); samVisit.setIsChildDischargedNrc(samDTO.getFields().getIs_child_discharged_nrc()); samVisit.setNrcDischargeDate(samDTO.getFields().getNrc_discharge_date()); @@ -709,7 +782,8 @@ public List saveAllIfa(List dtoList, Intege savedList.forEach(data -> { Timestamp visitTimestamp = Timestamp.valueOf(data.getIfaProvisionDate().atStartOfDay()); - if(data.getIfaBottleCount().equals("10.0")){ + + if(data!=null){ incentiveLogicService.incentiveForGiveingIFA( data.getBeneficiaryId(), visitTimestamp, @@ -774,8 +848,10 @@ private IfaDistributionDTO mapToDTO(IfaDistribution entity) { fields.setIfa_provision_date(entity.getIfaProvisionDate().format(FORMATTER)); } fields.setMcp_card_upload(entity.getMcpCardUpload()); - fields.setIfa_bottle_count(Double.valueOf(entity.getIfaBottleCount())); + if(entity.getIfaBottleCount()!=null){ + fields.setIfa_bottle_count(Double.valueOf(entity.getIfaBottleCount())); + } dto.setFields(fields); return dto; } @@ -803,7 +879,10 @@ private IfaDistribution mapToEntity(IfaDistributionDTO dto, Integer userId) { entity.setIfaProvisionDate(null); } } - entity.setIfaBottleCount(dto.getFields().getIfa_bottle_count().toString()); + if(dto.getFields().getIfa_bottle_count()!=null){ + entity.setIfaBottleCount(dto.getFields().getIfa_bottle_count().toString()); + + } entity.setMcpCardUpload(dto.getFields().getMcp_card_upload()); } @@ -930,12 +1009,32 @@ private void checkAndAddHbncIncentives(List hbncVisits, Integer userI } if (stateCode.equals(StateCode.CG.getStateCode())) { - if (hbncVisit.getVisit_day().equals("42nd Day")) { - IncentiveActivity visitActivityCH = incentivesRepo.findIncentiveMasterByNameAndGroup("HBNC_0_42_DAYS", GroupName.ACTIVITY.getDisplayName()); + if ("42nd Day".equals(hbncVisit.getVisit_day())) { + IncentiveActivity visitActivityCH = incentivesRepo + .findIncentiveMasterByNameAndGroup("HBNC_0_42_DAYS", GroupName.ACTIVITY.getDisplayName()); createIncentiveRecordforHbncVisit(hbncVisit, benId, visitActivityCH, "HBNC_0_42_DAYS"); + // 1. Child ka record + List childList = + beneficiaryRepo.findByBenficieryid(hbncVisit.getBeneficiaryId()); + + if (childList != null && !childList.isEmpty()) { + RMNCHBeneficiaryDetailsRmnch child = childList.get(childList.size() - 1); + + // 2. Same household se mother + RMNCHBeneficiaryDetailsRmnch mother = beneficiaryRepo + .findByHouseoldId(child.getHouseoldId()).stream() + .filter(b -> !"NewBorn".equalsIgnoreCase(b.getRegistrationType()) + && "Postnatal Mother".equalsIgnoreCase(b.getReproductiveStatus())) + .findFirst() + .orElse(null); + if (mother != null) { + generateHighRiskPostpartumIncentives(mother.getBenficieryid(), true, userId); + } + } } + } @@ -945,6 +1044,80 @@ private void checkAndAddHbncIncentives(List hbncVisits, Integer userI } + private void generateHighRiskPostpartumIncentives(Long motherBenId, boolean childHbnc42Done, Integer userId) { + + if (!childHbnc42Done) return; + + List motherPnc = pncVisitRepo.findByBenId(motherBenId); + + PNCVisit visit42 = motherPnc.stream() + .filter(v -> Integer.valueOf(42).equals(v.getPncPeriod())) + .findFirst() + .orElse(null); + + if (visit42 == null) return; + + boolean hasDangerSign = motherPnc.stream() + .anyMatch(v -> v.getMotherDangerSign() != null + && !v.getMotherDangerSign().isEmpty() + && v.getPncPeriod() != null + && v.getPncPeriod() >= 1 + && v.getPncPeriod() <= 42); + + if (hasDangerSign) { + IncentiveActivity act1 = incentivesRepo.findIncentiveMasterByNameAndGroup( + "HIGH_RISK_POSTPARTUM_CARE", GroupName.ACTIVITY.getDisplayName()); + if (act1 != null) { + addIncenticeRecord(visit42, userId, act1); // date 42 wale visit ki + } + } + + boolean motherAlive = !Boolean.TRUE.equals(visit42.getMotherDeath()); + + boolean hrpConfirmed = ancVisitRepo.findByBenId(motherBenId).stream() + .filter(a -> a.getAncVisit() != null && a.getAncVisit() >= 1 && a.getAncVisit() <= 7) + .anyMatch(a -> Boolean.TRUE.equals(a.getIsHrpConfirmed())); + + if (motherAlive && hrpConfirmed) { + IncentiveActivity act2 = incentivesRepo.findIncentiveMasterByNameAndGroup( + "HIGH_RISK_POSTPARTUM_HEALTH_CHECK", GroupName.ACTIVITY.getDisplayName()); + if (act2 != null) { + addIncenticeRecord(visit42, userId, act2); + } + } + } + + @Transactional + private void addIncenticeRecord(PNCVisit ect, Integer userId, IncentiveActivity antaraActivity) { + + String lockKey = antaraActivity.getId() + "_" + ect.getBenId() + "_" + ect.getPncDate(); + + Object lock = lockMap.computeIfAbsent(lockKey, k -> new Object()); + + synchronized (lock) { + IncentiveActivityRecord record = recordRepo + .findRecordByActivityIdCreatedDateBenId(antaraActivity.getId(), ect.getPncDate(), ect.getBenId(),userId); + + if (record == null) { + record = new IncentiveActivityRecord(); + record.setActivityId(antaraActivity.getId()); + record.setCreatedDate(ect.getPncDate()); + record.setCreatedBy(ect.getCreatedBy()); + record.setStartDate(ect.getPncDate()); + record.setEndDate(ect.getPncDate()); + record.setUpdatedDate(ect.getPncDate()); + record.setUpdatedBy(ect.getCreatedBy()); + record.setBenId(ect.getBenId()); + record.setAshaId(userId); + record.setAmount(Long.valueOf(antaraActivity.getRate())); + recordRepo.save(record); + } + } + + lockMap.remove(lockKey, lock); + } + + private void checkAndAddIncentives(List vaccinationList) { String createdBy = vaccinationList.get(0).getCreatedBy(); diff --git a/src/main/java/com/iemr/flw/service/impl/CoupleServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/CoupleServiceImpl.java index 44926729a..e12a1c87c 100644 --- a/src/main/java/com/iemr/flw/service/impl/CoupleServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/CoupleServiceImpl.java @@ -16,6 +16,7 @@ import com.iemr.flw.repo.iemr.*; import com.iemr.flw.service.CoupleService; import com.iemr.flw.service.UserService; +import org.joda.time.DateTime; import org.modelmapper.ModelMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +27,8 @@ import java.io.IOException; import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -75,8 +78,8 @@ public String registerEligibleCouple(List eligibleCoupleDTOs, List ecrList = new ArrayList<>(); if(!eligibleCoupleDTOs.isEmpty()){ for (EligibleCoupleDTO it : eligibleCoupleDTOs) { - Integer userId = userRepo.getUserIdByName(it.getCreatedBy()); - Integer stateId = userService.getUserDetail(userId).getStateId(); + Integer userId = userRepo.getUserIdByName(it.getCreatedBy()); + Integer stateId = userService.getUserDetail(userId).getStateId(); EligibleCoupleRegister existingECR = eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(it.getBenId()); @@ -157,16 +160,16 @@ public String registerEligibleCouple(List eligibleCoupleDTOs, if (Boolean.TRUE.equals(existingECR.getIsKitHandedOver()) && ((existingECR.getKitPhoto1() != null && !existingECR.getKitPhoto1().isEmpty()) || (existingECR.getKitPhoto2() != null && !existingECR.getKitPhoto2().isEmpty()))) { - if(stateId.equals(StateCode.AM.getStateCode())){ - IncentiveActivity activity = - incentivesRepo.findIncentiveMasterByNameAndGroup( - "FP_NP_KIT", - GroupName.FAMILY_PLANNING.getDisplayName()); - - if (activity != null) { - createIncentiveRecord(existingECR, activity); - } - } + if(stateId.equals(StateCode.AM.getStateCode())){ + IncentiveActivity activity = + incentivesRepo.findIncentiveMasterByNameAndGroup( + "FP_NP_KIT", + GroupName.FAMILY_PLANNING.getDisplayName()); + + if (activity != null) { + createIncentiveRecordForKitHandOver(existingECR, activity); + } + } if(stateId.equals(StateCode.CG.getStateCode())){ IncentiveActivity activity = incentivesRepo.findIncentiveMasterByNameAndGroup( @@ -174,7 +177,7 @@ public String registerEligibleCouple(List eligibleCoupleDTOs, GroupName.ACTIVITY.getDisplayName()); if (activity != null) { - createIncentiveRecord(existingECR, activity); + createIncentiveRecordForKitHandOver(existingECR, activity); } } @@ -332,20 +335,25 @@ private void checkIncentiveForChildGap(Integer stateId, String userName,List new Object()); + if (activity == null + || eligibleCoupleDTO == null + || eligibleCoupleDTO.getKitHandedOverDate() == null + || eligibleCoupleDTO.getKitHandedOverDate().trim().isEmpty()) { + return; + } + + LocalDate kitHandOverDate = + LocalDate.parse(eligibleCoupleDTO.getKitHandedOverDate()); + + Timestamp kitHandOverTimestamp = + Timestamp.valueOf(kitHandOverDate.atStartOfDay()); + synchronized (lock){ + if (activity != null) { + if(eligibleCoupleDTO.getKitHandedOverDate()!=null && !eligibleCoupleDTO.getKitHandedOverDate().isEmpty()){ + + } + Integer userId = userRepo.getUserIdByName(eligibleCoupleDTO.getCreatedBy()); + + IncentiveActivityRecord record = recordRepo + .findRecordByActivityIdCreatedDateBenId(activity.getId(), kitHandOverTimestamp, eligibleCoupleDTO.getBenId(),userId); + + if (record == null) { + record = new IncentiveActivityRecord(); + record.setActivityId(activity.getId()); + record.setCreatedDate(kitHandOverTimestamp); + record.setCreatedBy(eligibleCoupleDTO.getCreatedBy()); + record.setStartDate(kitHandOverTimestamp); + record.setEndDate(kitHandOverTimestamp); + record.setUpdatedDate(eligibleCoupleDTO.getCreatedDate()); + record.setUpdatedBy(eligibleCoupleDTO.getCreatedBy()); + record.setBenId(eligibleCoupleDTO.getBenId()); + record.setAshaId(userId); + record.setAmount(Long.valueOf(activity.getRate())); + recordRepo.save(record); + } + } + } + lockMap.remove(lockKey, lock); + + + } @Override @@ -475,7 +531,7 @@ private void checkAndAddAntaraIncentive(EligibleCoupleTracking ect) { }else if (ect.getAntraDose().contains("Dose-5")) { IncentiveActivity antaraActivity4 = incentivesRepo.findIncentiveMasterByNameAndGroup("FP_ANC_MPA5", GroupName.FAMILY_PLANNING.getDisplayName()); - + if (antaraActivity4 != null) { addIncenticeRecord(ect, userId, antaraActivity4); @@ -489,29 +545,29 @@ private void checkAndAddAntaraIncentive(EligibleCoupleTracking ect) { addIncenticeRecord(ect, userId, antaraActivity4CH); } } - - + + } else if (ect.getMethodOfContraception() != null && ect.getMethodOfContraception().equals("MALE STERILIZATION")) { if(stateId.equals(StateCode.AM.getStateCode())){ IncentiveActivity maleSterilizationActivityAM = incentivesRepo.findIncentiveMasterByNameAndGroup("FP_MALE_STER", "FAMILY PLANNING"); - + if (maleSterilizationActivityAM != null) { addIncenticeRecord(ect, userId, maleSterilizationActivityAM); } - + } if(stateId.equals(StateCode.CG.getStateCode())){ - + IncentiveActivity maleSterilizationActivityCH = incentivesRepo.findIncentiveMasterByNameAndGroup("FP_MALE_STER", GroupName.ACTIVITY.getDisplayName()); - + if (maleSterilizationActivityCH != null) { addIncenticeRecord(ect, userId, maleSterilizationActivityCH); } } - + } else if (ect.getMethodOfContraception() != null && ect.getMethodOfContraception().equals("FEMALE STERILIZATION")) { if(stateId.equals(StateCode.CG.getStateCode())){ IncentiveActivity femaleSterilizationActivityCH = @@ -531,8 +587,8 @@ private void checkAndAddAntaraIncentive(EligibleCoupleTracking ect) { } - - + + } else if (ect.getMethodOfContraception() != null && ect.getMethodOfContraception().equals("MiniLap")) { if(stateId.equals(StateCode.AM.getStateCode())){ IncentiveActivity miniLapActivity = @@ -541,7 +597,7 @@ private void checkAndAddAntaraIncentive(EligibleCoupleTracking ect) { addIncenticeRecord(ect, userId, miniLapActivity); } } - + } else if (ect.getMethodOfContraception() != null && ect.getMethodOfContraception().equals("Condom")) { if(stateId.equals(StateCode.AM.getStateCode())){ IncentiveActivity comdomActivity = @@ -550,7 +606,7 @@ private void checkAndAddAntaraIncentive(EligibleCoupleTracking ect) { addIncenticeRecord(ect, userId, comdomActivity); } } - + } else if (ect.getMethodOfContraception() != null && ect.getMethodOfContraception().equals("Copper T (IUCD)")) { if(stateId.equals(StateCode.AM.getStateCode())){ IncentiveActivity copperTActivity = @@ -559,7 +615,7 @@ private void checkAndAddAntaraIncentive(EligibleCoupleTracking ect) { addIncenticeRecord(ect, userId, copperTActivity); } } - + } if (ect.getMethodOfContraception() != null && (ect.getMethodOfContraception().contains("POST PARTUM STERILIZATION (PPS WITHIN 7 DAYS OF DELIVERY)") || ect.getMethodOfContraception().contains("MiniLap") || ect.getMethodOfContraception().contains("MALE STERILIZATION") || ect.getMethodOfContraception().contains("FEMALE STERILIZATION"))) { if(stateId.equals(StateCode.AM.getStateCode())){ @@ -578,8 +634,8 @@ private void checkAndAddAntaraIncentive(EligibleCoupleTracking ect) { addIncenticeRecord(ect, userId, limitiing2ChildActivityCH); } } - - + + } } diff --git a/src/main/java/com/iemr/flw/service/impl/DeathReportsServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DeathReportsServiceImpl.java index b7eee7dd0..cfc772142 100644 --- a/src/main/java/com/iemr/flw/service/impl/DeathReportsServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DeathReportsServiceImpl.java @@ -81,37 +81,6 @@ public String registerCDR(List cdrDTOs) { } } - public String updateCDRUploadFiles(CdrDTO cdrDTOs, Long incentiveActivityId) { - try { - Optional cdrOptional = cdrRepo.findById(cdrDTOs.getId()); - CDR cdr = new CDR(); - if (cdrOptional.isPresent()) { - cdr = cdrOptional.get(); - if (cdrDTOs.getCdrImage() != null && !cdrDTOs.getCdrImage().isEmpty()) { - cdr.setCdrImage(cdrDTOs.getCdrImage()); - } - - if (cdrDTOs.getCdrImage2() != null && !cdrDTOs.getCdrImage2().isEmpty()) { - cdr.setCdrImage2(cdrDTOs.getCdrImage2()); - } - - if (cdrDTOs.getDeathCertImage1() != null && !cdrDTOs.getDeathCertImage1().isEmpty()) { - cdr.setDeathCertImage1(cdrDTOs.getDeathCertImage1()); - } - - if (cdrDTOs.getDeathCertImage2() != null && !cdrDTOs.getDeathCertImage2().isEmpty()) { - cdr.setDeathCertImage2(cdrDTOs.getDeathCertImage2()); - } - } - cdrRepo.save(cdr); - - pendindDocService.updateIncentive(incentiveActivityId); - - return "no of cdr details update: "; - } catch (Exception e) { - return "error while saving cdr details: " + e.getMessage(); - } - } @Override public String registerMDSR(List mdsrDTOs) { @@ -141,43 +110,6 @@ public String registerMDSR(List mdsrDTOs) { } } - public String updateMDSR(MdsrDTO mdsrDTO, Long incentiveActivityId) { - try { - - Optional mdsrOptional = mdsrRepo.findById(mdsrDTO.getId()); - MDSR mdsr = new MDSR(); - - if (mdsrOptional.isPresent()) { - - mdsr = mdsrOptional.get(); - - if (mdsrDTO.getMdsr1File() != null && !mdsrDTO.getMdsr1File().isEmpty()) { - mdsr.setMdsr1File(mdsrDTO.getMdsr1File()); - } - - if (mdsrDTO.getMdsr2File() != null && !mdsrDTO.getMdsr2File().isEmpty()) { - mdsr.setMdsr2File(mdsrDTO.getMdsr2File()); - } - - if (mdsrDTO.getMdsrDeathCertFile() != null && !mdsrDTO.getMdsrDeathCertFile().isEmpty()) { - mdsr.setMdsrDeathCertFile(mdsrDTO.getMdsrDeathCertFile()); - } - - } else { - return "MDSR record not found with id: " + mdsrDTO.getId(); - } - - mdsrRepo.save(mdsr); - - // incentive update - pendindDocService.updateIncentive(incentiveActivityId); - - return "MDSR updated successfully for id: " + mdsrDTO.getId(); - - } catch (Exception e) { - return "error while updating mdsr details: " + e.getMessage(); - } - } @Override public List getCdrRecords(GetBenRequestHandler dto) { diff --git a/src/main/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImpl.java index 3170deba0..06b8c87f8 100644 --- a/src/main/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImpl.java @@ -139,11 +139,11 @@ public void checkAndAddJsyIncentive(List delOutList) { IncentiveActivity institutionalDeliveryActivityAM = incentivesRepo.findIncentiveMasterByNameAndGroup("MH_MOTIVATE_INST_DEL", GroupName.MATERNAL_HEALTH.getDisplayName()); IncentiveActivity institutionalDeliveryActivityCH = incentivesRepo.findIncentiveMasterByNameAndGroup("INST_DELIVERY_ESCORT", GroupName.ACTIVITY.getDisplayName()); if (deliveryOutcome.getPlaceOfDelivery() != null) { - String placeOfDelivery = deliveryOutcome.getPlaceOfDelivery(); + String placeOfDelivery = deliveryOutcome.getPlaceOfDelivery().toLowerCase(); if (placeOfDelivery != null && - (!placeOfDelivery.equalsIgnoreCase("home") || - !placeOfDelivery.equalsIgnoreCase("in transit") || + (!placeOfDelivery.equalsIgnoreCase("home") && + !placeOfDelivery.equalsIgnoreCase("in transit") && !placeOfDelivery.equalsIgnoreCase("other private hospital"))) { // Institutional delivery (eligible case) @@ -170,7 +170,14 @@ public void checkAndAddJsyIncentive(List delOutList) { if (deliveryOutcome.getDeliveryOutcome() == 1) { createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityJSY1); if (deliveryOutcome.getPlaceOfDelivery() != null) { - createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY1); + String placeOfDelivery = deliveryOutcome.getPlaceOfDelivery().toLowerCase(); + if(placeOfDelivery != null && + (!placeOfDelivery.equalsIgnoreCase("home") && + !placeOfDelivery.equalsIgnoreCase("in transit") && + !placeOfDelivery.equalsIgnoreCase("other private hospital"))){ + createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY1); + + } } } } @@ -181,8 +188,15 @@ public void checkAndAddJsyIncentive(List delOutList) { if (deliveryOutcome.getDeliveryOutcome() == 2) { createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityJSY2); if (deliveryOutcome.getPlaceOfDelivery() != null) { + String placeOfDelivery = deliveryOutcome.getPlaceOfDelivery().toLowerCase(); + if(placeOfDelivery != null && + (!placeOfDelivery.equalsIgnoreCase("home") && + !placeOfDelivery.equalsIgnoreCase("in transit") && + !placeOfDelivery.equalsIgnoreCase("other private hospital"))){ + createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY2); + + } - createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY2); } } } @@ -192,7 +206,14 @@ public void checkAndAddJsyIncentive(List delOutList) { if (deliveryOutcome.getDeliveryOutcome() == 3) { createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityJSY3); if (deliveryOutcome.getPlaceOfDelivery() != null) { - createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY3); + String placeOfDelivery = deliveryOutcome.getPlaceOfDelivery().toLowerCase(); + if(placeOfDelivery != null && + (!placeOfDelivery.equalsIgnoreCase("home") && + !placeOfDelivery.equalsIgnoreCase("in transit") && + !placeOfDelivery.equalsIgnoreCase("other private hospital"))){ + createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY3); + + } } } } @@ -202,7 +223,14 @@ public void checkAndAddJsyIncentive(List delOutList) { if (deliveryOutcome.getDeliveryOutcome() == 4) { createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityJSY4); if (deliveryOutcome.getPlaceOfDelivery() != null) { - createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY4); + String placeOfDelivery = deliveryOutcome.getPlaceOfDelivery().toLowerCase(); + if(placeOfDelivery != null && + (!placeOfDelivery.equalsIgnoreCase("home") && + !placeOfDelivery.equalsIgnoreCase("in transit") && + !placeOfDelivery.equalsIgnoreCase("other private hospital"))){ + createIncentiveRecordforJsy(deliveryOutcome, deliveryOutcome.getBenId(), incentiveActivityInstJSY4); + + } } } diff --git a/src/main/java/com/iemr/flw/service/impl/DiagnosticOrderServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DiagnosticOrderServiceImpl.java index e206afbcb..913a747a2 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiagnosticOrderServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiagnosticOrderServiceImpl.java @@ -18,19 +18,19 @@ import com.iemr.flw.integration.provider.DiagnosticPushResult; import com.iemr.flw.masterEnum.DiagnosticOrderStatus; import com.iemr.flw.masterEnum.DiagnosticOrderType; -import com.iemr.flw.dto.iemr.UserServiceRoleDTO; import com.iemr.flw.repo.identity.BeneficiaryRepo; import com.iemr.flw.repo.iemr.DiagnosticOrderRepo; import com.iemr.flw.repo.iemr.DiagnosticResultRepo; import com.iemr.flw.repo.iemr.EmployeeMasterRepo; import com.iemr.flw.repo.iemr.TBSuspectedRepo; -import com.iemr.flw.repo.iemr.UserServiceRoleRepo; import com.iemr.flw.service.CampConfigService; import com.iemr.flw.service.DiagnosticDocumentService; import com.iemr.flw.service.DiagnosticOrderService; import com.iemr.flw.service.TBStopVisitService; import com.iemr.flw.utils.JwtUtil; -import com.google.common.util.concurrent.Striped; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -38,12 +38,15 @@ import org.springframework.stereotype.Service; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; @Service public class DiagnosticOrderServiceImpl implements DiagnosticOrderService { @@ -60,6 +63,12 @@ public class DiagnosticOrderServiceImpl implements DiagnosticOrderService { DiagnosticOrderStatus.FAILED.name(), DiagnosticOrderStatus.CLOSED.name()); + // Statuses the scheduler polls and the daily cutoff expires — matches findXrayDueForPoll / + // findTrueNatDueForPoll / findAllOutstandingOrders. + private static final Set OUTSTANDING_STATUSES = Set.of( + DiagnosticOrderStatus.PENDING.name(), + DiagnosticOrderStatus.IN_PROGRESS.name()); + // Invalid test outcomes: the result is still recorded (result row, tb_suspected write-back, documents) // but the order is CLOSED instead of COMPLETED, so a retest can be pushed. The vendor reports an // invalid MTB/MDR_RIF run as "Error-2"; a manual entry uses "Invalid/Error". @@ -123,19 +132,36 @@ public DiagnosticOrder createAndPushOrderAsSystem(DiagnosticOrderRequestDto requ // two simultaneous pushes for the same beneficiary could both pass the dedup check (or both create a // visit for today) and send the same patient to the vendor twice — shown as duplicate names on the // TrueNat machine. Keyed by beneficiary, not orderType, because the visit is shared across order types. + // (release-3.9.3 keys its lock by beneficiary+orderType because there the client supplies the + // visitCode; here the server creates the visit, so the key must be the beneficiary alone.) // In-memory, so it only serialises within this JVM: correct while orders are created only by the - // single van server that can reach the vendor. Striped keeps the lock registry a fixed size for the - // JVM's lifetime: the same beneficiary always maps to the same stripe, while unrelated beneficiaries - // that happen to share a stripe merely wait on each other briefly. Stripes are reentrant, and no path - // ever holds two beneficiaries' locks at once, so sharing a stripe can't deadlock. - private static final int BENEFICIARY_LOCK_STRIPES = 1024; - private final Striped beneficiaryOrderLocks = Striped.lock(BENEFICIARY_LOCK_STRIPES); + // single van server that can reach the vendor. Same mechanism as release-3.9.3: one ReentrantLock per + // beneficiary, held weakly so an idle beneficiary's lock is garbage-collected — any thread waiting on + // or holding it keeps a strong reference, so concurrent callers always share the same instance. + // User requests wait at most LOCK_WAIT_SECONDS and then fail with a retry message, instead of piling + // up behind a slow vendor push (bounded by the EmrLiteRestTemplateConfig timeouts). + private final LoadingCache beneficiaryOrderLocks = CacheBuilder.newBuilder() + .weakValues() + .build(CacheLoader.from(beneficiaryId -> new ReentrantLock())); + + private static final long LOCK_WAIT_SECONDS = 30; private T withBeneficiaryLock(Long beneficiaryId, Callable action) throws Exception { - if (beneficiaryId == null) { - throw new IllegalArgumentException("beneficiaryId is required"); + Lock lock = beneficiaryLock(beneficiaryId); + if (!lock.tryLock(LOCK_WAIT_SECONDS, TimeUnit.SECONDS)) { + throw new IllegalStateException("Another diagnostic order operation is still in progress for beneficiaryId=" + + beneficiaryId + " — please retry shortly"); + } + try { + return action.call(); + } finally { + lock.unlock(); } - Lock lock = beneficiaryOrderLocks.get(beneficiaryId); + } + + // For background work with no caller to retry it (the automatic retest): waits as long as it takes. + private T withBeneficiaryLockBlocking(Long beneficiaryId, Callable action) throws Exception { + Lock lock = beneficiaryLock(beneficiaryId); lock.lock(); try { return action.call(); @@ -144,6 +170,27 @@ private T withBeneficiaryLock(Long beneficiaryId, Callable action) throws } } + // For the poll scheduler: never waits. A busy beneficiary (user push/manual entry in progress) is + // simply left for the next tick, rather than stalling the single scheduler thread behind it. + private Optional withBeneficiaryLockIfFree(Long beneficiaryId, Callable action) throws Exception { + Lock lock = beneficiaryLock(beneficiaryId); + if (!lock.tryLock()) { + return Optional.empty(); + } + try { + return Optional.ofNullable(action.call()); + } finally { + lock.unlock(); + } + } + + private Lock beneficiaryLock(Long beneficiaryId) { + if (beneficiaryId == null) { + throw new IllegalArgumentException("beneficiaryId is required"); + } + return beneficiaryOrderLocks.getUnchecked(beneficiaryId); + } + private DiagnosticOrder createAndPushOrder(DiagnosticOrderRequestDto request, String createdBy) throws Exception { return withBeneficiaryLock(request.getBeneficiaryId(), () -> createAndPushOrderLocked(request, createdBy)); } @@ -407,7 +454,28 @@ public DiagnosticOrderResultDto processResult(DiagnosticOrder order, DiagnosticP private DiagnosticOrderResultDto processResult(DiagnosticOrder order, DiagnosticPollResult pollResult, boolean writeBackWhenClosed, String actingUser) throws Exception { Optional existingResult = diagnosticResultRepo.findByExternalOrderIdAndDeletedFalse(order.getExternalOrderId()); + // A poll that brings nothing new must not rewrite the rows: every rewrite resets processed + // (so MMU re-uploads the order) and would re-send it to every tablet. + boolean resultUnchanged = existingResult.isPresent() && sameResult(existingResult.get(), pollResult); + boolean orderUnchanged = sameOrderStatus(order, pollResult); DiagnosticResult result = existingResult.orElseGet(DiagnosticResult::new); + if (resultUnchanged && orderUnchanged && !writeBackWhenClosed) { + ingestAssets(order, pollResult); + diagnosticOrderRepo.recordUnchangedPoll(order.getId(), new Timestamp(System.currentTimeMillis()), + order.getRetryCount()); + DiagnosticOrderResultDto unchanged = new DiagnosticOrderResultDto(); + unchanged.setExternalOrderId(order.getExternalOrderId()); + unchanged.setOrderType(order.getOrderType()); + unchanged.setStatus(order.getStatus()); + unchanged.setProviderStatus(result.getProviderStatus()); + unchanged.setResultSummary(result.getResultSummary()); + unchanged.setErrorMessage(order.getErrorMessage()); + unchanged.setReasonToClose(order.getReasonToClose()); + unchanged.setTbPresence(result.getTbPresence()); + unchanged.setTbConfidence(result.getTbConfidence()); + unchanged.setDrugResistancePresence(result.getDrugResistancePresence()); + return unchanged; + } result.setExternalOrderId(order.getExternalOrderId()); result.setBeneficiaryId(order.getBeneficiaryId()); result.setProviderStatus(pollResult.getStatus().name()); @@ -433,6 +501,9 @@ private DiagnosticOrderResultDto processResult(DiagnosticOrder order, Diagnostic diagnosticResultRepo.save(result); if (result.getVanSerialNo() == null) diagnosticResultRepo.updateVanSerialNo(result.getId()); } catch (DataIntegrityViolationException dive) { + // Only a first insert can lose a race (unique external_order_id). Any other integrity + // failure means the result was not saved, so the order must not move on without it. + if (existingResult.isPresent()) throw dive; logger.warn("Lost result upsert race for externalOrderId={}", order.getExternalOrderId()); result = diagnosticResultRepo.findByExternalOrderIdAndDeletedFalse(order.getExternalOrderId()).orElse(result); } @@ -515,8 +586,90 @@ private void ingestAssets(DiagnosticOrder order, DiagnosticPollResult pollResult } } + // The scheduler loads its due-for-poll batch at the start of a tick and polls each order in turn, so + // by the time an order's turn comes it may have been completed/closed by a manual entry. Polling and + // saving that stale copy would overwrite the manual result (whole-row save, no @Version), so the + // order is re-read under the beneficiary lock and skipped unless it is still awaiting a result. + // Failures are recorded on that same fresh row, inside the lock, for the same reason. @Override public DiagnosticPollResult pollOnce(DiagnosticOrder order) throws Exception { + Optional polled = withBeneficiaryLockIfFree(order.getBeneficiaryId(), () -> { + Optional current = findOutstandingOrder(order.getId()); + if (current.isEmpty()) { + logger.info("Skipping poll for orderId={}: no longer PENDING/IN_PROGRESS", order.getId()); + return null; + } + DiagnosticOrder fresh = current.get(); + try { + return pollOnceLocked(fresh); + } catch (Exception e) { + int retries = (fresh.getRetryCount() == null ? 0 : fresh.getRetryCount()) + 1; + if (java.util.Objects.equals(fresh.getErrorMessage(), e.getMessage())) { + // Same failure as last time: only the attempt is recorded (see recordUnchangedPoll). + diagnosticOrderRepo.recordUnchangedPoll(fresh.getId(), new Timestamp(System.currentTimeMillis()), retries); + } else { + fresh.setRetryCount(retries); + fresh.setLastPolledAt(new Timestamp(System.currentTimeMillis())); + fresh.setErrorMessage(e.getMessage()); + fresh.setModifiedBy("SYSTEM"); + fresh.setProcessed("N"); + diagnosticOrderRepo.save(fresh); + } + throw e; + } + }); + if (polled.isEmpty()) { + logger.debug("Poll for orderId={} skipped or deferred (beneficiary busy / order resolved)", order.getId()); + } + return polled.orElse(null); + } + + // Same stale-copy problem as pollOnce: the daily expiry batch is loaded up front, so each order is + // re-read under the lock and only closed if it is still outstanding. + @Override + public void expireOrder(DiagnosticOrder order, String reason) throws Exception { + withBeneficiaryLock(order.getBeneficiaryId(), () -> { + Optional current = findOutstandingOrder(order.getId()); + if (current.isEmpty()) { + logger.info("Skipping expiry for orderId={}: no longer PENDING/IN_PROGRESS", order.getId()); + return null; + } + DiagnosticOrder fresh = current.get(); + fresh.setStatus(DiagnosticOrderStatus.CLOSED.name()); + fresh.setReasonToClose(reason); + fresh.setLastPolledAt(new Timestamp(System.currentTimeMillis())); + fresh.setModifiedBy("SYSTEM"); + fresh.setProcessed("N"); + diagnosticOrderRepo.save(fresh); + notifyProviderOrderClosed(fresh, reason); + return null; + }); + } + + private static boolean sameResult(DiagnosticResult stored, DiagnosticPollResult polled) { + return java.util.Objects.equals(stored.getProviderStatus(), polled.getStatus() == null ? null : polled.getStatus().name()) + && java.util.Objects.equals(stored.getResultSummary(), polled.getResultSummary()) + && java.util.Objects.equals(stored.getTbPresence(), polled.getTbPresence()) + && java.util.Objects.equals(stored.getTbConfidence(), polled.getTbConfidence()) + && java.util.Objects.equals(stored.getDrugResistancePresence(), polled.getDrugResistancePresence()) + && (polled.getAssets() == null || polled.getAssets().isEmpty()); + } + + private static boolean sameOrderStatus(DiagnosticOrder order, DiagnosticPollResult polled) { + return polled.getStatus() != null + && polled.getStatus().name().equals(order.getStatus()) + && java.util.Objects.equals(order.getErrorMessage(), polled.getErrorMessage()) + && (polled.getProviderOrderId() == null + || polled.getProviderOrderId().equals(order.getProviderOrderId())); + } + + private Optional findOutstandingOrder(Long orderId) { + return diagnosticOrderRepo.findById(orderId) + .filter(o -> !Boolean.TRUE.equals(o.getDeleted())) + .filter(o -> OUTSTANDING_STATUSES.contains(o.getStatus())); + } + + private DiagnosticPollResult pollOnceLocked(DiagnosticOrder order) throws Exception { // A null pushResponseJson means the order was never actually confirmed as pushed to the // vendor (e.g. a stale/legacy row) — polling the vendor for it would be meaningless, so // fail it outright instead of burning a vendor call every tick. @@ -575,15 +728,33 @@ private DiagnosticOrder resolveOrder(Long beneficiaryId, String orderType, Long @Override public DiagnosticOrderResultDto triggerManualPoll(Long beneficiaryId, String orderType, Long visitCode) throws Exception { - DiagnosticOrder order = resolveOrder(beneficiaryId, orderType, visitCode); - DiagnosticProvider provider = providerFactory.getProvider(order.getProviderCode()); - DiagnosticPollResult pollResult = provider.pollResult(order, true); - boolean invalid = closeIfInvalidPolledResult(order, pollResult); - return processResult(order, pollResult, invalid, "SYSTEM"); + // Locked, and the order read inside the lock, so this can't interleave with a manual entry or a + // scheduler poll for the same beneficiary and save over what they just wrote. + return withBeneficiaryLock(beneficiaryId, () -> { + DiagnosticOrder order = resolveOrder(beneficiaryId, orderType, visitCode); + if (DiagnosticOrderStatus.MANUAL_ENTRY.name().equals(order.getStatus())) { + throw new IllegalStateException("Cannot poll a MANUAL_ENTRY order — no vendor is involved, " + + "submit the result via manualResult instead"); + } + if (order.getManuallyEnteredBy() != null) { + throw new IllegalStateException("Cannot poll this order: its result was entered manually by " + + order.getManuallyEnteredBy() + " and a vendor poll would overwrite it"); + } + DiagnosticProvider provider = providerFactory.getProvider(order.getProviderCode()); + DiagnosticPollResult pollResult = provider.pollResult(order, true); + boolean invalid = closeIfInvalidPolledResult(order, pollResult); + return processResult(order, pollResult, invalid, "SYSTEM"); + }); } @Override public DiagnosticOrder retryPoll(Long beneficiaryId, String orderType, Long visitCode) throws Exception { + // Locked so the terminal-status check and the reset to PENDING can't straddle a manual entry + // that completes the order in between (which would reopen it for polling). + return withBeneficiaryLock(beneficiaryId, () -> retryPollLocked(beneficiaryId, orderType, visitCode)); + } + + private DiagnosticOrder retryPollLocked(Long beneficiaryId, String orderType, Long visitCode) throws Exception { DiagnosticOrder order = resolveOrder(beneficiaryId, orderType, visitCode); String status = order.getStatus(); @@ -642,6 +813,41 @@ public DiagnosticOrderResultDto getOrderResult(Long beneficiaryId, String orderT return dto; } + @Override + public java.util.Map> getOrderRecordsByIds(java.util.Collection ids) { + java.util.Map> out = new java.util.LinkedHashMap<>(); + for (DiagnosticOrder order : diagnosticOrderRepo.findAllById(ids)) { + if (Boolean.TRUE.equals(order.getDeleted())) continue; + DiagnosticOrderResultDto dto = toResultDto(order); + if (order.getExternalOrderId() != null) { + diagnosticResultRepo.findByExternalOrderIdAndDeletedFalse(order.getExternalOrderId()).ifPresent(result -> { + dto.setProviderStatus(result.getProviderStatus()); + dto.setResultSummary(result.getResultSummary()); + dto.setTbPresence(result.getTbPresence()); + dto.setTbConfidence(result.getTbConfidence()); + dto.setDrugResistancePresence(result.getDrugResistancePresence()); + }); + } + java.util.Map m = new java.util.LinkedHashMap<>(); + m.put("id", order.getId()); + m.put("beneficiaryId", order.getBeneficiaryId()); + m.put("visitCode", order.getVisitCode()); + m.put("createdDate", order.getCreatedDate()); + m.put("externalOrderId", dto.getExternalOrderId()); + m.put("orderType", dto.getOrderType()); + m.put("status", dto.getStatus()); + m.put("providerStatus", dto.getProviderStatus()); + m.put("resultSummary", dto.getResultSummary()); + m.put("errorMessage", dto.getErrorMessage()); + m.put("reasonToClose", dto.getReasonToClose()); + m.put("tbPresence", dto.getTbPresence()); + m.put("tbConfidence", dto.getTbConfidence()); + m.put("drugResistancePresence", dto.getDrugResistancePresence()); + out.put(order.getId(), m); + } + return out; + } + private DiagnosticOrderResultDto toResultDto(DiagnosticOrder order) { DiagnosticOrderResultDto dto = new DiagnosticOrderResultDto(); dto.setExternalOrderId(order.getExternalOrderId()); @@ -656,16 +862,25 @@ private DiagnosticOrderResultDto toResultDto(DiagnosticOrder order) { public DiagnosticOrderStatusSummaryDto getOrderStatusSummary(String orderType, Integer villageId, Integer providerServiceMapId) { DiagnosticOrderType type = DiagnosticOrderType.fromCode(orderType); - List awaitingProviderResult = diagnosticOrderRepo - .findBeneficiaryIdsAwaitingProviderResult(type.name(), villageId, providerServiceMapId); - List completed = diagnosticOrderRepo - .findBeneficiaryIdsCompleted(type.name(), villageId, providerServiceMapId); - List failed = diagnosticOrderRepo - .findBeneficiaryIdsFailed(type.name(), villageId, providerServiceMapId); - List closed = diagnosticOrderRepo - .findBeneficiaryIdsClosed(type.name(), villageId, providerServiceMapId); - List awaitingManualEntry = diagnosticOrderRepo - .findBeneficiaryIdsAwaitingManualEntry(type.name(), villageId, providerServiceMapId); + List awaitingProviderResult = new ArrayList<>(); + List completed = new ArrayList<>(); + List failed = new ArrayList<>(); + List closed = new ArrayList<>(); + List awaitingManualEntry = new ArrayList<>(); + // One query instead of one per bucket; buckets are the same status groups the per-bucket + // queries used (matched case-insensitively, like the database comparison was). + for (Object[] row : diagnosticOrderRepo.findLatestOrderStatusByBeneficiary(type.name(), villageId, providerServiceMapId)) { + Long beneficiaryId = row[0] != null ? ((Number) row[0]).longValue() : null; + String status = row[1] != null ? row[1].toString().toUpperCase(java.util.Locale.ROOT) : ""; + switch (status) { + case "PENDING", "IN_PROGRESS" -> awaitingProviderResult.add(beneficiaryId); + case "COMPLETED" -> completed.add(beneficiaryId); + case "FAILED" -> failed.add(beneficiaryId); + case "CLOSED", "CANCELLED", "EXPIRED", "REFUSED" -> closed.add(beneficiaryId); + case "MANUAL_ENTRY" -> awaitingManualEntry.add(beneficiaryId); + default -> { } + } + } return new DiagnosticOrderStatusSummaryDto(awaitingProviderResult, completed, failed, closed, awaitingManualEntry); } @@ -704,6 +919,12 @@ public DiagnosticOrderResultDto submitManualResult(ManualDiagnosticResultRequest return toResultDto(closed); } + return withBeneficiaryLock(request.getBeneficiaryId(), + () -> submitManualResultLocked(request, actingUserName)); + } + + private DiagnosticOrderResultDto submitManualResultLocked(ManualDiagnosticResultRequestDto request, + String actingUserName) throws Exception { DiagnosticOrder order = findLatestOrder(request.getBeneficiaryId(), request.getOrderType()); if (DiagnosticOrderStatus.COMPLETED.name().equals(order.getStatus())) { throw new IllegalStateException("Cannot submit manual result: order is already COMPLETED (beneficiaryId=" @@ -743,7 +964,7 @@ private static boolean isInvalidResult(String orderTypeCode, String resultSummar // pushed to the vendor straight away. Never throws — a failure here must not undo the close. private void pushRetestOrder(DiagnosticOrder closed) { try { - withBeneficiaryLock(closed.getBeneficiaryId(), () -> { + withBeneficiaryLockBlocking(closed.getBeneficiaryId(), () -> { pushRetestOrderLocked(closed); return null; }); diff --git a/src/main/java/com/iemr/flw/service/impl/DiagnosticPollSchedulerService.java b/src/main/java/com/iemr/flw/service/impl/DiagnosticPollSchedulerService.java index 2259d934c..1a36e8cd0 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiagnosticPollSchedulerService.java +++ b/src/main/java/com/iemr/flw/service/impl/DiagnosticPollSchedulerService.java @@ -2,7 +2,6 @@ import com.iemr.flw.domain.iemr.DiagnosticOrder; import com.iemr.flw.integration.provider.DiagnosticProviderFactory; -import com.iemr.flw.masterEnum.DiagnosticOrderStatus; import com.iemr.flw.masterEnum.DiagnosticOrderType; import com.iemr.flw.repo.iemr.DiagnosticOrderRepo; import com.iemr.flw.service.DiagnosticOrderService; @@ -11,18 +10,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.springframework.web.client.ResourceAccessException; import java.sql.Timestamp; import java.time.LocalDate; -import java.time.ZoneId; import java.util.List; +import java.util.function.Supplier; -// Every due order (status PENDING) is polled on every tick from the moment it's created — -// there is no per-order initial delay or rolling give-up window anymore. A tick is skipped entirely -// for an order-type family if diagnostic.provider.xray / diagnostic.provider.truenat has no active -// vendor configured right now (checked live via DiagnosticProviderFactory, not any value stored on the -// order row). Once a day, expireOutstandingOrders sweeps up everything still PENDING (for -// order-type families that do have an active vendor) and marks it CLOSED. @Service public class DiagnosticPollSchedulerService { @@ -39,50 +33,70 @@ public class DiagnosticPollSchedulerService { @Scheduled(fixedDelayString = "${diagnostic.poll.xray.tick-ms:15000}") public void pollXrayOrders() { - if (noActiveVendor(DiagnosticOrderType.XRAY_CHEST)) { - logger.debug("No active vendor configured for XRAY_CHEST — skipping poll tick"); - return; - } - List candidates = diagnosticOrderRepo.findXrayDueForPoll(); - logger.info("XRAY poll tick: {} order(s) in due-for-poll queue", candidates.size()); - if (candidates.isEmpty()) return; - - for (DiagnosticOrder order : candidates) { - pollSingle(order); - } - logger.info("Polled {} pending XRAY diagnostic orders", candidates.size()); + pollDueOrders("XRAY", DiagnosticOrderType.XRAY_CHEST, + diagnosticOrderRepo::findXrayDueForPoll); } @Scheduled(fixedDelayString = "${diagnostic.poll.truenat.tick-ms:60000}") public void pollTrueNatOrders() { - if (noActiveVendor(DiagnosticOrderType.MTB)) { - logger.debug("No active vendor configured for TrueNat — skipping poll tick"); + pollDueOrders("TrueNat", DiagnosticOrderType.MTB, + diagnosticOrderRepo::findTrueNatDueForPoll); + } + + private void pollDueOrders(String label, DiagnosticOrderType orderType, Supplier> dueOrders) { + String providerCode = providerFactory.getProviderCodeForOrderType(orderType); + if (providerCode == null || providerCode.isBlank()) { + logger.debug("No active vendor configured for {} — skipping poll tick", label); return; } - List candidates = diagnosticOrderRepo.findTrueNatDueForPoll(); - logger.info("TrueNat poll tick: {} order(s) in due-for-poll queue", candidates.size()); + List candidates = dueOrders.get(); + logger.info("{} poll tick: {} order(s) in due-for-poll queue", label, candidates.size()); if (candidates.isEmpty()) return; - for (DiagnosticOrder order : candidates) { - pollSingle(order); + if (!providerFactory.getProvider(providerCode).checkHealth(orderType)) { + logger.warn("{} vendor ping failed — skipping poll tick, {} order(s) left for the next tick", + label, candidates.size()); + return; } - logger.info("Polled {} pending TrueNat diagnostic orders", candidates.size()); - } - @Scheduled(cron = "${diagnostic.poll.expiry-cron:0 50 23 * * *}") - public void expireOutstandingOrders() { - List outstanding = diagnosticOrderRepo.findAllOutstandingOrders(); - int expired = 0; - for (DiagnosticOrder order : outstanding) { - if (noActiveVendor(DiagnosticOrderType.fromCode(order.getOrderType()))) { - continue; // awaiting manual entry — not subject to the polling cutoff + int polled = 0; + for (DiagnosticOrder order : candidates) { + polled++; + if (!pollSingle(order)) { + logger.warn("{} vendor unreachable mid-tick — stopping, {} order(s) left for the next tick", + label, candidates.size() - polled); + break; } - expire(order); - expired++; - } - if (expired > 0) { - logger.warn("Daily polling cutoff reached: marked {} outstanding order(s) CLOSED", expired); } + logger.info("Polled {} of {} pending {} diagnostic orders", polled, candidates.size(), label); + } + + // Auto-closure of outstanding orders at the daily cutoff is disabled for now — orders are no longer + // closed automatically. Uncomment this job (and expire() below) to re-enable it. + // @Scheduled(cron = "${diagnostic.poll.expiry-cron:0 50 23 * * *}") + // public void expireOutstandingOrders() { + // List outstanding = diagnosticOrderRepo.findAllOutstandingOrders(startOfToday(), startOfTomorrow()); + // int expired = 0; + // for (DiagnosticOrder order : outstanding) { + // if (noActiveVendor(DiagnosticOrderType.fromCode(order.getOrderType()))) { + // continue; // awaiting manual entry — not subject to the polling cutoff + // } + // expire(order); + // expired++; + // } + // if (expired > 0) { + // logger.warn("Daily polling cutoff reached: marked {} outstanding order(s) CLOSED", expired); + // } + // } + + // The (disabled) daily close only handles orders created today. JVM default zone on purpose: it's the + // same clock created_date is stamped with (@CreationTimestamp), so the day boundaries line up. + private static Timestamp startOfToday() { + return Timestamp.valueOf(LocalDate.now().atStartOfDay()); + } + + private static Timestamp startOfTomorrow() { + return Timestamp.valueOf(LocalDate.now().plusDays(1).atStartOfDay()); } private boolean noActiveVendor(DiagnosticOrderType orderType) { @@ -90,30 +104,31 @@ private boolean noActiveVendor(DiagnosticOrderType orderType) { return providerCode == null || providerCode.isBlank(); } - private void expire(DiagnosticOrder order) { - String reason = "Order validity expired for the day " + LocalDate.now(ZoneId.of("Asia/Kolkata")); - order.setStatus(DiagnosticOrderStatus.CLOSED.name()); - order.setReasonToClose(reason); - order.setLastPolledAt(new Timestamp(System.currentTimeMillis())); - order.setModifiedBy("SYSTEM"); - order.setProcessed("N"); - diagnosticOrderRepo.save(order); - diagnosticOrderService.notifyProviderOrderClosed(order, reason); - } + // Used only by the (disabled) auto-closure job above. expireOrder re-reads the order under the + // beneficiary lock, so an order completed manually after this batch was loaded is left alone. + // private void expire(DiagnosticOrder order) { + // String reason = "Order validity expired for the day " + LocalDate.now(ZoneId.of("Asia/Kolkata")); + // try { + // diagnosticOrderService.expireOrder(order, reason); + // } catch (Exception e) { + // logger.error("Expiry failed for orderId={}: {}", order.getId(), e.getMessage()); + // } + // } - private void pollSingle(DiagnosticOrder order) { + // Returns false only when the vendor couldn't be reached at all (connect/read timeout, refused + // connection) — the caller stops the tick then. Any other failure is specific to this order + // (bad data, vendor-side rejection), so it's recorded and the tick carries on. + private boolean pollSingle(DiagnosticOrder order) { try { - // pollOnce now saves the result/order fields and ingests any documents itself — - // nothing left to do here on success. diagnosticOrderService.pollOnce(order); + return true; + } catch (ResourceAccessException e) { + logger.error("Vendor unreachable polling orderId={}: {}", order.getId(), e.getMessage()); + return false; } catch (Exception e) { logger.error("Poll failed for orderId={}: {}", order.getId(), e.getMessage()); - order.setRetryCount(order.getRetryCount() + 1); - order.setLastPolledAt(new Timestamp(System.currentTimeMillis())); - order.setErrorMessage(e.getMessage()); - order.setModifiedBy("SYSTEM"); - order.setProcessed("N"); - diagnosticOrderRepo.save(order); + return true; } } + } \ 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 35746b2f8..4cbfeb874 100644 --- a/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DiseaseControlServiceImpl.java @@ -48,6 +48,7 @@ import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @@ -352,41 +353,51 @@ private LeprosyFollowUp saveLeprosyFollowUpData(LeprosyFollowUpDTO data) { entity.setLastModDate( data.getLastModDate() != null ? data.getLastModDate() : new Timestamp(System.currentTimeMillis())); if(!leprosyFollowUpRepository.findByBenId(entity.getBenId()).isEmpty()){ - if(leprosyFollowUpRepository.findByBenId(entity.getBenId()).size()>=6 && leprosyFollowUpRepository.findByBenId(entity.getBenId()).size()<=12){ - Integer userId = userRepo.getUserIdByName(entity.getCreatedBy()); - IncentiveActivityRecord incentiveActivityRecord = - incentiveLogicService.incentiveForLeprosyPaucibacillaryConfirmed( - entity.getBenId(), - entity.getTreatmentEndDate(), - entity.getTreatmentEndDate(), - userId); - - if (incentiveActivityRecord != null) { - logger.info("Incentive processed for Screening Leprosy successfully. RecordId={}", - incentiveActivityRecord.getId()); - } else { - logger.info("Incentive not created"); + if(entity.getTypeOfLeprosy().equals("PB (Paucibacillary)")){ + if(leprosyFollowUpRepository.findByBenId(entity.getBenId()).size()>=6 && leprosyFollowUpRepository.findByBenId(entity.getBenId()).size()<=12){ + Integer userId = userRepo.getUserIdByName(entity.getCreatedBy()); + IncentiveActivityRecord incentiveActivityRecord = + incentiveLogicService.incentiveForLeprosyPaucibacillaryConfirmed( + entity.getBenId(), + entity.getTreatmentEndDate(), + entity.getTreatmentEndDate(), + userId); + + if (incentiveActivityRecord != null) { + logger.info("Incentive processed for Screening Leprosy successfully. RecordId={}", + incentiveActivityRecord.getId()); + } else { + logger.info("Incentive not created"); + } } } + } if(!leprosyFollowUpRepository.findByBenId(entity.getBenId()).isEmpty()){ - if(leprosyFollowUpRepository.findByBenId(entity.getBenId()).size()>=12){ - Integer userId = userRepo.getUserIdByName(entity.getCreatedBy()); - IncentiveActivityRecord incentiveActivityRecord = - incentiveLogicService.incentiveForLeprosyMultibacillaryConfirmed( - entity.getBenId(), - entity.getTreatmentEndDate(), - entity.getTreatmentEndDate(), - userId); - - if (incentiveActivityRecord != null) { - logger.info("Incentive processed for Screening Leprosy successfully. RecordId={}", - incentiveActivityRecord.getId()); - } else { - logger.info("Incentive not created"); + if(entity.getTypeOfLeprosy().equals("MB (Multibacillary)")) { + if (entity.getTreatmentStartDate() != null && entity.getTreatmentEndDate() != null) { + if (isEligibleForIncentive(entity.getTreatmentStartDate(), entity.getTreatmentEndDate())) { + Integer userId = userRepo.getUserIdByName(entity.getCreatedBy()); + IncentiveActivityRecord incentiveActivityRecord = + incentiveLogicService.incentiveForLeprosyMultibacillaryConfirmed( + entity.getBenId(), + entity.getTreatmentEndDate(), + entity.getTreatmentEndDate(), + userId); + + if (incentiveActivityRecord != null) { + logger.info("Incentive processed for Screening Leprosy successfully. RecordId={}", + incentiveActivityRecord.getId()); + } else { + logger.info("Incentive not created"); + } + } } } + + + } return entity; } @@ -401,6 +412,24 @@ public String saveLeprosyFollowUp(LeprosyFollowUpDTO dto) { } + public boolean isEligibleForIncentive(Date treatmentStartDate, Date endDate) { + + if (treatmentStartDate == null || endDate == null) { + return false; + } + + LocalDate start = treatmentStartDate.toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + + LocalDate end = endDate.toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + + return end.isAfter(start.plusMonths(12)) + || end.isEqual(start.plusMonths(12)); + } + @Override public List getAllLeprosyData(String createdBy) { logger.info("Fetching leprosy data for createdBy: " + createdBy); @@ -1249,6 +1278,36 @@ private void checkAndAddIncentives(ScreeningMalaria diseaseScreening) { } + if (diseaseScreening.getCaseStatus().equals("Suspected")) { + if (!diseaseScreening.getMalariaTestType().isEmpty()) { + if (stateId.equals(StateCode.AM.getStateCode())) { + if (incentiveActivityForCollectSlideAM != null) { + addIncentive(incentiveActivityForCollectSlideAM, diseaseScreening); + + } + } + + if (stateId.equals(StateCode.CG.getStateCode())) { + if (incentiveActivityForCollectSlideCG != null) { + addIncentive(incentiveActivityForCollectSlideCG, diseaseScreening); + + } + } + + + } + + }else { + if(!diseaseScreening.getMalariaTestType().isEmpty()){ + if (stateId.equals(StateCode.CG.getStateCode())) { + if (incentiveActivityForCollectSlideCG != null) { + addIncentive(incentiveActivityForCollectSlideCG, diseaseScreening); + + } + } + + } + } if (diseaseScreening.getCaseStatus().equals("Suspected")) { if (!diseaseScreening.getMalariaTestType().isEmpty()) { if (stateId.equals(StateCode.AM.getStateCode())) { diff --git a/src/main/java/com/iemr/flw/service/impl/DynamicFormResponseServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/DynamicFormResponseServiceImpl.java index f74088a6d..b14ab50c1 100644 --- a/src/main/java/com/iemr/flw/service/impl/DynamicFormResponseServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/DynamicFormResponseServiceImpl.java @@ -61,6 +61,8 @@ import java.sql.Timestamp; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -460,6 +462,24 @@ private QuestionOption resolveOption( // ---- read helpers ---- + @Override + public Map getResponsesByIds(Collection responseIds) { + List responses = formResponseRepo.findAllById(responseIds); + Set versionIds = responses.stream() + .map(FormResponse::getVersionId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + Map versionNumberById = formVersionRepo.findAllById(versionIds).stream() + .collect(Collectors.toMap(FormVersion::getVersionId, FormVersion::getVersionNumber)); + Map out = new LinkedHashMap<>(); + for (FormResponse r : responses) { + FormResponseDTO dto = buildFormResponseDTO(r, loadSectionResponseDTOs(r.getResponseId())); + dto.setVersionNumber(versionNumberById.get(r.getVersionId())); + out.put(r.getResponseId(), dto); + } + return out; + } + private List loadSectionResponseDTOs(Long responseId) { List sections = sectionResponseRepo.findByResponseId(responseId); if (sections.isEmpty()) { diff --git a/src/main/java/com/iemr/flw/service/impl/FormFollowUpNotificationScheduler.java b/src/main/java/com/iemr/flw/service/impl/FormFollowUpNotificationScheduler.java index 5062c9fb1..431ad3920 100644 --- a/src/main/java/com/iemr/flw/service/impl/FormFollowUpNotificationScheduler.java +++ b/src/main/java/com/iemr/flw/service/impl/FormFollowUpNotificationScheduler.java @@ -25,6 +25,7 @@ import com.iemr.flw.dto.iemr.FormResponseDTO; import com.iemr.flw.repo.iemr.DynamicFormRepo; import com.iemr.flw.service.DynamicFormResponseService; +import com.iemr.flw.service.NotificationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; diff --git a/src/main/java/com/iemr/flw/service/impl/IRSRoundServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/IRSRoundServiceImpl.java index 26a5c1c74..5e8853a5b 100644 --- a/src/main/java/com/iemr/flw/service/impl/IRSRoundServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/IRSRoundServiceImpl.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -26,7 +27,7 @@ public List addRounds(List dtos,Integer userId,String use } return irsRoundRepository.saveAll( dtos.stream() - .map(dto -> new IRSRound(null, dto.getDate(), dto.getRounds(), dto.getHouseholdId(),userName,userId)) + .map(dto -> new IRSRound(null, dto.getDate(), dto.getRounds(), dto.getHouseholdId(),userName,userId ,userName,getCurrentTimestamp())) .collect(Collectors.toList()) ); } @@ -35,4 +36,8 @@ public List addRounds(List dtos,Integer userId,String use public List getRounds(Long householdId) { return irsRoundRepository.findByHouseholdId(householdId); } + + private Timestamp getCurrentTimestamp() { + return new Timestamp(System.currentTimeMillis()); + } } diff --git a/src/main/java/com/iemr/flw/service/impl/IncentiveLogicImpl.java b/src/main/java/com/iemr/flw/service/impl/IncentiveLogicImpl.java index b6e4b7c7a..7200d4e04 100644 --- a/src/main/java/com/iemr/flw/service/impl/IncentiveLogicImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/IncentiveLogicImpl.java @@ -85,6 +85,48 @@ public IncentiveActivityRecord incentiveForLeprosyPaucibacillaryConfirmed( } } + @Override + public IncentiveActivityRecord incentiveForTbPreventiveFollowUp(Long benId, Timestamp treatmentStartDate, Timestamp treatmentEndDate, Integer userId) { + try { + Integer stateCode = userService.getUserDetail(userId).getStateId(); + + if (stateCode == null) { + logger.warn("State code is null for user: {}", userId); + return null; + } + + String activityName = "TPT_PROVIDING"; + + if (stateCode.equals(StateCode.CG.getStateCode())) { + return processIncentive( + activityName, + GroupName.ACTIVITY.getDisplayName(), + benId, + treatmentStartDate, + treatmentStartDate, + userId); + } + + if (stateCode.equals(StateCode.AM.getStateCode())) { + return processIncentive( + activityName, + GroupName.UMBRELLA_PROGRAMMES.getDisplayName(), + benId, + treatmentStartDate, + treatmentEndDate, + userId); + } + + // state not supported + logger.info("No incentive mapping for stateCode: {}", stateCode); + return null; + + } catch (Exception e) { + logger.error("Check TB Incentive Exception: ", e); + return null; + } + } + @Override public IncentiveActivityRecord incentiveForTbSuspected(Long benId, Timestamp treatmentStartDate, Timestamp treatmentEndDate, Integer userId) { try { diff --git a/src/main/java/com/iemr/flw/service/impl/IncentiveMonthlyScheduler.java b/src/main/java/com/iemr/flw/service/impl/IncentiveMonthlyScheduler.java new file mode 100644 index 000000000..2624f5644 --- /dev/null +++ b/src/main/java/com/iemr/flw/service/impl/IncentiveMonthlyScheduler.java @@ -0,0 +1,49 @@ +package com.iemr.flw.service.impl; + +import com.iemr.flw.repo.iemr.IncentiveRecordRepo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@Component +@RequiredArgsConstructor +@Slf4j +public class IncentiveMonthlyScheduler { + + private static final ZoneId INDIA_ZONE = + ZoneId.of("Asia/Kolkata"); + @Autowired + private IncentiveRecordRepo recordRepo; + + @Scheduled(cron = "0 5 0 1 * *", zone = "Asia/Kolkata") + public void resetRejectedIncentivesForNewMonth() { + + LocalDate firstDayOfCurrentMonth = + LocalDate.now(INDIA_ZONE).withDayOfMonth(1); + + Timestamp currentMonthStart = + Timestamp.valueOf(firstDayOfCurrentMonth.atStartOfDay()); + + Timestamp updatedDate = + Timestamp.from(ZonedDateTime.now(INDIA_ZONE).toInstant()); + + int updatedCount = + recordRepo.resetRejectedIncentivesForNewMonth( + currentMonthStart, + updatedDate, + "SYSTEM" + ); + + log.info( + "Rejected incentives reset successfully. Updated records: {}", + updatedCount + ); + } +} \ No newline at end of file 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 f109c88a8..b89cb137f 100644 --- a/src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/IncentiveServiceImpl.java @@ -1,23 +1,19 @@ package com.iemr.flw.service.impl; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.iemr.flw.domain.identity.RMNCHMBeneficiarydetail; import com.iemr.flw.domain.iemr.*; import com.iemr.flw.dto.identity.GetBenRequestHandler; import com.iemr.flw.dto.iemr.*; import com.iemr.flw.masterEnum.GroupName; -import com.iemr.flw.masterEnum.IncentiveApprovalStatus; -import com.iemr.flw.masterEnum.IncentiveName; import com.iemr.flw.masterEnum.StateCode; import com.iemr.flw.repo.identity.BeneficiaryRepo; import com.iemr.flw.repo.iemr.*; import com.iemr.flw.service.IncentiveService; import com.iemr.flw.service.MaaMeetingService; +import com.iemr.flw.service.NotificationService; import com.iemr.flw.service.UserService; import com.iemr.flw.utils.JwtUtil; -import jakarta.persistence.criteria.CriteriaBuilder; import org.modelmapper.ModelMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,11 +26,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import java.math.BigInteger; import java.sql.Timestamp; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.*; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -85,6 +78,12 @@ public class IncentiveServiceImpl implements IncentiveService { private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>(); + @Autowired + private NotificationService notificationService; + + @Autowired + private SupervisorDashboardRepo dashboardRepo; + // ================= MASTER SAVE ================= @@ -150,7 +149,12 @@ public String getIncentiveMaster(IncentiveRequestDTO incentiveRequestDTO) { dto.setName(mapping.getName()); if (isCG) { - dto.setGroupName(""); + if (inc.getGroupCategoryName() != null + && !inc.getGroupCategoryName().isEmpty()) { + dto.setGroupName(inc.getGroupCategoryName()); + } else { + dto.setGroupName(inc.getGroup()); + } } else if (isAM) { dto.setGroupName(mapping.getGroup()); } @@ -173,11 +177,14 @@ public String getIncentiveMaster(IncentiveRequestDTO incentiveRequestDTO) { } else { if (isCG) { - dto.setGroupName(""); - + if (inc.getGroupCategoryName() != null + && !inc.getGroupCategoryName().isEmpty()) { + dto.setGroupName(inc.getGroupCategoryName()); + } else { + dto.setGroupName(inc.getGroup()); + } } else { dto.setGroupName(inc.getGroup()); - } } @@ -192,34 +199,32 @@ public String getIncentiveMaster(IncentiveRequestDTO incentiveRequestDTO) { } return null; } - @Override public String getAllIncentivesByUserId(GetBenRequestHandler request) { int page = 0; - int size = 20; + int size = 200; // bumped from 20 — fewer round trips for large histories Page pageResult; List finalDtos = new ArrayList<>(); - Integer stateCode = userService.getUserDetail(request.getAshaId()).getStateId(); - String userName = userService.getUserDetail(request.getAshaId()).getUserName(); - try { + // Fetch user detail ONCE instead of twice + UserServiceRoleDTO userDetail = userService.getUserDetail(request.getAshaId()); + Integer stateCode = userDetail.getStateId(); + String userName = userDetail.getUserName(); + try { if (stateCode.equals(StateCode.AM.getStateCode())) { checkMonthlyAshaIncentive(request.getAshaId()); } if (stateCode.equals(StateCode.CG.getStateCode())) { checkMonthlyAshaIncentiveForCg(request.getAshaId()); - } - } catch (Exception e) { logger.error("Error in checkMonthlyAshaIncentive: ", e); } try { addIncentiveForIronTablets(request.getAshaId()); - incentiveOfNcdReferal(request.getAshaId(), request.getVillageID()); - + incentiveOfNcdReferal(request.getAshaId(), request.getVillageID(),request); } catch (Exception e) { logger.error("Error in incentiveOfNcdReferal: ", e); } @@ -227,17 +232,9 @@ public String getAllIncentivesByUserId(GetBenRequestHandler request) { boolean isCG = stateCode != null && stateCode.equals(StateCode.CG.getStateCode()); do { + Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending()); - Pageable pageable = PageRequest.of( - page, - size, - Sort.by("id").descending() // latest first - ); - - pageResult = recordRepo.findRecordsByAsha( - request.getAshaId(), - pageable - ); + pageResult = recordRepo.findRecordsByAsha(request.getAshaId(), pageable); List entities = pageResult.getContent(); @@ -245,159 +242,402 @@ public String getAllIncentivesByUserId(GetBenRequestHandler request) { return new Gson().toJson(Collections.emptyList()); } - // Step 2: Collect all activityIds — fetch valid ones in ONE query + // Bulk fetch valid activity ids List activityIds = entities.stream() .map(IncentiveActivityRecord::getActivityId) .distinct() .collect(Collectors.toList()); - // Single bulk query instead of N individual findIncentiveMasterById() calls Set validActivityIds = isCG ? incentivesRepo.findValidActivityIds(activityIds, true) : incentivesRepo.findValidActivityIds(activityIds, false); - // Filter entities based on valid activity IDs entities = entities.stream() .filter(e -> validActivityIds.contains(e.getActivityId())) .collect(Collectors.toList()); - // Step 3: Collect all benIds that need name lookup (name == null and benId > 0) + // 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()); - // Step 4: Bulk fetch all beneficiary names in ONE query instead of 3 queries per record Map benIdToNameMap = new HashMap<>(); - if (!benIdsToFetch.isEmpty()) { - List benDetails = beneficiaryRepo.findBenNamesByBenIds(benIdsToFetch); + Map regIdToBenIdMap = new HashMap<>(); + + if (benIdsToFetch != null && !benIdsToFetch.isEmpty()) { + + // First, fetch names using BeneficiaryId + List benDetails = + beneficiaryRepo.findBenNamesByBenIds(benIdsToFetch); + + Set foundBenIds = new HashSet<>(); + for (Object[] row : benDetails) { Long benId = ((Number) row[0]).longValue(); - String first = row[1] != null ? (String) row[1] : ""; - String last = row[2] != null ? (String) row[2] : ""; + + 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); + } + + // IDs not found as BeneficiaryId + List unresolvedIds = benIdsToFetch.stream() + .filter(Objects::nonNull) + .filter(id -> !foundBenIds.contains(id)) + .distinct() + .toList(); + + if (!unresolvedIds.isEmpty()) { + + // Resolve BenRegId -> BeneficiaryId + List mappings = + beneficiaryRepo.getBenIdsFromRegIds(unresolvedIds); + + Set resolvedBenIds = new HashSet<>(); + + for (Object[] row : mappings) { + Long regId = ((Number) row[0]).longValue(); + Long benId = ((Number) row[1]).longValue(); + + regIdToBenIdMap.put(regId, benId); + resolvedBenIds.add(benId); + } + + // 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(); + + String first = row[1] != null ? row[1].toString() : ""; + String last = row[2] != null ? row[2].toString() : ""; + + 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); } } - // Step 5: Map entities to DTOs + // Map entities to DTOs List dtos = entities.stream().map(entry -> { if (entry.getName() == null) { - if (entry.getBenId() != null && entry.getBenId() > 0) { - String name = benIdToNameMap.getOrDefault(entry.getBenId(), ""); + Long originalId = entry.getBenId(); + + if (originalId != null && originalId > 0) { + + Long actualBenId = regIdToBenIdMap.getOrDefault( + originalId, originalId); + + String name = benIdToNameMap.getOrDefault(actualBenId, ""); + + entry.setBenId(actualBenId); entry.setName(name); + if (isCG) { entry.setIsEligible(true); } + } else { entry.setName(""); } - if (entry.getVerifiedByUserId() != null) { - entry.setSupervisorRole(userRepo.getUserRole(entry.getVerifiedByUserId()).get(0).getRoleName()); - entry.setVerifiedByUserName(userRepo.getUserRole(entry.getVerifiedByUserId()).get(0).getName()); + if (entry.getVerifiedByUserId() != null) { + UserServiceRoleDTO role = verifierRoleMap.get(entry.getVerifiedByUserId()); + if (role != null) { + entry.setSupervisorRole(role.getRoleName()); + entry.setVerifiedByUserName(role.getName()); + } } + 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()); - finalDtos.addAll(dtos); + finalDtos.addAll(dtos); page++; } while (pageResult.hasNext()); - - Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); return gson.toJson(finalDtos); } + + @Transactional + public void resetRejectedIncentivesForPreviousMonths() { + + ZoneId zoneId = ZoneId.of("Asia/Kolkata"); + + LocalDate firstDayOfCurrentMonth = + LocalDate.now(zoneId).withDayOfMonth(1); + + Timestamp currentMonthStart = + Timestamp.valueOf(firstDayOfCurrentMonth.atStartOfDay()); + + Timestamp now = + Timestamp.from(ZonedDateTime.now(zoneId).toInstant()); + + recordRepo.resetRejectedIncentivesForNewMonth( + currentMonthStart, + now, + "SYSTEM" + ); + } // ================= GROUPED SUMMARY ================= - @Override - public String getAllIncentivesGroupedSummary(GetBenRequestHandler request) { + @Transactional(readOnly = true) + public String getAllIncentivesGroupedSummary(GetBenRequestHandler request, Integer userId) { - LocalDate start = LocalDate.of(request.getYear(), request.getMonth(), 1); - LocalDate end = start.withDayOfMonth(start.lengthOfMonth()); + String roleName = userService.getUserDetail(userId).getRoleName(); + Integer reqStatus = request.getApprovalStatus(); - Timestamp startTs = Timestamp.valueOf(start.atStartOfDay()); - Timestamp endTs = Timestamp.valueOf(end.atTime(23, 59, 59)); + Integer stateId = userRepo.getUserRole(request.getUserId()).get(0).getStateId(); + boolean isCG = stateId != null && stateId.intValue() == StateCode.CG.getStateCode(); - Integer villageID = userRepo.getUserRole(request.getUserId()).get(0).getStateId(); - boolean isCG = villageID != null && villageID.intValue() == StateCode.CG.getStateCode(); + logger.info("Request getAllIncentivesGroupedSummary: {}", reqStatus); List records = recordRepo.findRecordsByAsha(request.getUserId()) .stream() - .filter(r -> r.getCreatedDate() != null - && r.getEndDate() != null - && r.getEndDate().toLocalDateTime().getMonthValue() == request.getMonth() - && r.getEndDate().toLocalDateTime().getYear() == request.getYear() - && r.getIsClaimed()) - .toList(); - + .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()); - // Bulk fetch valid activity IDs — state wise List activityIds = records.stream() .map(IncentiveActivityRecord::getActivityId) .collect(Collectors.toList()); - Set validActivityIds = isCG - ? incentivesRepo.findValidActivityIds(activityIds, true) - : incentivesRepo.findValidActivityIds(activityIds, false); + Set validActivityIds = incentivesRepo.findValidActivityIds(activityIds, isCG); + if (validActivityIds == null) { + validActivityIds = Collections.emptySet(); + } + final Set validIds = validActivityIds; - // Filter records based on valid activity IDs - records = records.stream() - .filter(r -> validActivityIds.contains(r.getActivityId())) - .collect(Collectors.toList()); + if (isCG) { + + if ("ASHA Supervisor".equalsIgnoreCase(roleName)) { + + if (Integer.valueOf(104).equals(reqStatus)) { + records = records.stream() + .filter(r -> validIds.contains(r.getActivityId()) + && Boolean.TRUE.equals(r.getIsDefaultActivity()) + && Objects.equals(r.getApprovalStatus(), 102)) + .collect(Collectors.toList()); + } else if (Integer.valueOf(102).equals(reqStatus)) { + records = records.stream() + .filter(r -> validIds.contains(r.getActivityId()) + && Boolean.TRUE.equals(r.getIsDefaultActivity()) + && Objects.equals(r.getApprovalStatus(), 102)) + .collect(Collectors.toList()); + + }else { + records = records.stream() + .filter(r -> validIds.contains(r.getActivityId()) + && Boolean.TRUE.equals(r.getIsDefaultActivity())) + .collect(Collectors.toList()); + } + + } else if ("ANM".equalsIgnoreCase(roleName) || "CHO".equalsIgnoreCase(roleName)) { + + if (Integer.valueOf(102).equals(reqStatus) || Integer.valueOf(105).equals(reqStatus)) { + + records = records.stream() + .filter(r -> validIds.contains(r.getActivityId())) + + // default + approved + 102 -> 105 + .peek(r -> { + if (Objects.equals(r.getApprovalStatus(), 102) + && Boolean.TRUE.equals(r.getIsDefaultActivity()) + && Boolean.TRUE.equals(r.getIsApproved())) { + + logger.info("Changing record status 102 to 105: id={}, activityId={}", + r.getId(), r.getActivityId()); + r.setApprovalStatus(105); + } + }) + + .filter(r -> { + boolean isDefault = Boolean.TRUE.equals(r.getIsDefaultActivity()); + boolean isClaimed = Boolean.TRUE.equals(r.getIsClaimed()); + Integer recStatus = r.getApprovalStatus(); + + if (Integer.valueOf(102).equals(reqStatus)) { + return (!isDefault && Objects.equals(recStatus, 102) && isClaimed) + || Objects.equals(recStatus, 105); + } + + return Objects.equals(recStatus, 101) + || Objects.equals(recStatus, 105); + }) + .collect(Collectors.toList()); + + } else if (Integer.valueOf(104).equals(reqStatus)) { + + records = records.stream() + .filter(r -> validIds.contains(r.getActivityId())) + .filter(r -> { + boolean isDefault = Boolean.TRUE.equals(r.getIsDefaultActivity()); + boolean isApproved = Boolean.TRUE.equals(r.getIsApproved()); + + boolean status102 = Objects.equals(r.getApprovalStatus(), 102) + && (!isDefault || isApproved); + + boolean status105 = Objects.equals(r.getApprovalStatus(), 105) + && isDefault; + + boolean status104 = Objects.equals(r.getApprovalStatus(), 104); + + return status102 || status105 || status104; + }) + .collect(Collectors.toList()); + + } else { + records = records.stream() + .filter(r -> validIds.contains(r.getActivityId())) + .collect(Collectors.toList()); + } + + logger.info("ANM/CHO records after default activity filter: {}", records.size()); + } + + } else { + records = records.stream() + .filter(r -> validIds.contains(r.getActivityId())) + .collect(Collectors.toList()); + } Map> grouped = records.stream().collect(Collectors.groupingBy(IncentiveActivityRecord::getActivityId)); List> result = new ArrayList<>(); - for (var entry : grouped.entrySet()) { + for (Map.Entry> entry : grouped.entrySet()) { Long activityId = entry.getKey(); List list = entry.getValue(); - IncentiveActivity activity = - incentivesRepo.findById(activityId).orElse(null); - + IncentiveActivity activity = incentivesRepo.findById(activityId).orElse(null); if (activity == null) continue; + IncentiveActivityRecord latest = list.stream() + .max(Comparator.comparing(IncentiveActivityRecord::getCreatedDate)) + .orElse(null); + if (latest == null) continue; + + Integer approvalStatus = latest.getApprovalStatus() != null ? latest.getApprovalStatus() : 0; + boolean isApproved = Boolean.TRUE.equals(latest.getIsApproved()); + Long incentiveId = latest.getId(); + long total = list.stream() .mapToLong(r -> r.getAmount() != null ? r.getAmount() : 0) .sum(); Map map = new HashMap<>(); map.put("activityId", activityId); + map.put("incentiveId", incentiveId); map.put("activityDec", activity.getDescription()); - map.put("groupName", activity.getGroup()); map.put("claimCount", list.size()); map.put("totalAmount", total); map.put("amount", activity.getRate()); + if (isCG) { + if (activity.getGroupCategoryName() != null && !activity.getGroupCategoryName().isEmpty()) { + map.put("groupName", activity.getGroupCategoryName()); + } else { + map.put("groupName", activity.getGroup()); + } + map.put("isDefault", activity.getIsDefaultActivity()); + map.put("approvalStatus", approvalStatus); + map.put("isApproved", isApproved); + } else { + map.put("groupName", activity.getGroup()); + } + result.add(map); } return new Gson().toJson(result); } + /** + * Request ke status ke hisaab se kaunse DB status allow hain. + */ + private boolean matchesRequestedStatus(Integer recStatus, Integer reqStatus) { + + if (Integer.valueOf(104).equals(reqStatus)) { + // Overdue tab: 102, 104, 105 + return Objects.equals(recStatus, 102) + || Objects.equals(recStatus, 104) + || Objects.equals(recStatus, 105); + } + + if (Integer.valueOf(105).equals(reqStatus)) { + // Verified tab: 101, 105 + return Objects.equals(recStatus, 101) + || Objects.equals(recStatus, 105); + } + + if (Integer.valueOf(102).equals(reqStatus)) { + return Objects.equals(recStatus, 102) + || Objects.equals(recStatus, 105); + } + + return Objects.equals(recStatus, reqStatus); + } + + private boolean isAfter24Hours(Timestamp claimedDate) { + if (claimedDate == null) { + return false; + } + + long now = System.currentTimeMillis(); + long diff = now - claimedDate.getTime(); + + logger.info("Now: {}", new Timestamp(now)); + logger.info("ClaimedDate: {}", claimedDate); + logger.info("Diff(ms): {}", diff); + logger.info("Diff(hours): {}", diff / (1000 * 60 * 60.0)); + + return diff >= 24L * 60 * 60 * 1000; + } @Override public String getAllIncentivesGroupedActivity(GetBenRequestHandler request) { try { @@ -411,17 +651,75 @@ public String getAllIncentivesGroupedActivity(GetBenRequestHandler request) { List records = recordRepo.findRecordsByAsha(request.getUserId()) .stream() - .filter(r -> r.getActivityId() != null - && r.getActivityId().equals(request.getActivityId()) - && r.getCreatedDate() != null - && !r.getCreatedDate().before(startTs) - && r.getCreatedDate().before(endTs)) + .filter(r -> + r.getActivityId() != null + && Objects.equals( + r.getActivityId(), + request.getActivityId() + ) + && r.getCreatedDate() != null + && !r.getCreatedDate().before(startTs) + && r.getCreatedDate().before(endTs) + ) + .filter(r -> { + + Integer requestedStatus = + request.getApprovalStatus(); + + Integer recordStatus = + r.getApprovalStatus(); + + boolean isDefault = + Boolean.TRUE.equals( + r.getIsDefaultActivity() + ); + + boolean isApproved = + Boolean.TRUE.equals( + r.getIsApproved() + ); + + // Overdue + if (Objects.equals(requestedStatus, 104)) { + + // 102 non-default records OR approved default records + boolean status102 = + Objects.equals(recordStatus, 102) + && (!isDefault || isApproved); + + // 105 only for default activities + boolean status105 = + Objects.equals(recordStatus, 105) + && isDefault; + + // Existing overdue records + boolean status104 = + Objects.equals(recordStatus, 104); + + return status102 + || status105 + || status104; + } + + // Verified: 101 or 105 + if (Objects.equals(requestedStatus, 105)) { + return Objects.equals(recordStatus, 101) + || Objects.equals(recordStatus, 105); + } + + // Pending, rejected or other status + return Objects.equals( + recordStatus, + requestedStatus + ); + }) .collect(Collectors.toList()); if (records.isEmpty()) { return new Gson().toJson(new ArrayList<>()); } + // 🔹 Get beneficiary names Set benIds = records.stream() .map(IncentiveActivityRecord::getBenId) @@ -445,6 +743,7 @@ 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 description = activity != null ? activity.getDescription() : ""; // 🔹 Map result @@ -458,7 +757,7 @@ public String getAllIncentivesGroupedActivity(GetBenRequestHandler request) { } } - r.setGroupName(groupName); + r.setGroupName(groupCategoryName); r.setActivityDec(description); return modelMapper.map(r, IncentiveRecordDTO.class); @@ -553,18 +852,87 @@ public String updateIncentive(PendingActivityDTO pendingActivityDTO) { // ================= UPDATE CLAIM ================= @Transactional - public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boolean isClaimed, String token) { + public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boolean isClaimed, String token,String incentive) { + String title = null; try { LocalDate start = LocalDate.of(year, month, 1); LocalDate end = start.plusMonths(1); - int updated = recordRepo.updateClaimStatusByAshaAndDateRange( - ashaId, - isClaimed, - Timestamp.valueOf(LocalDateTime.now()), - Timestamp.valueOf(start.atStartOfDay()), - Timestamp.valueOf(end.atStartOfDay()) - ); + int updated=0; + + Timestamp claimedDate = Timestamp.valueOf(LocalDateTime.now()); + + if (incentive == null || incentive.isEmpty()) { + + // No incentive IDs → all incentives for selected month/date range + updated = recordRepo.updateClaimStatusByAshaAndDateRange( + ashaId, + isClaimed, + claimedDate, + Timestamp.valueOf(start.atStartOfDay()), + Timestamp.valueOf(end.atStartOfDay()) + ); + + } else { + + for (String incentiveId : incentive.split(",")) { + + Long id = Long.parseLong(incentiveId.trim()); + updated = recordRepo.updateClaimStatusByAshaAndIncentiveIds( + ashaId, + isClaimed, + claimedDate,id + ); + } + } + + if (updated > 0) { + + 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("month", String.valueOf(month)); + data.put("year", String.valueOf(year)); + data.put("approval_status", String.valueOf(102)); + data.put("priority", "HIGH"); + + title = "Incentive Claimed"; + + if (isClaimed) { + + String body = "Your incentive successfully claimed for month of " + + Month.of(month).name() + " " + year; + + notificationService.sendNotification( + "FLW", + "NA", + title, + body, + "INCENTIVE_CLAIMED", + "INCENTIVE", + dashboardRepo.getSupervisorUserIdByAshaId(ashaId) + ); + + String supervisorBody = + userService.getUserDetail(ashaId).getName() + + " successfully claimed incentive for month of " + + Month.of(month).name() + + " " + + year; + + notificationService.sendNotification( + "FLW", + "NA", + title, + supervisorBody, + "INCENTIVE_CLAIMED", + "INCENTIVE", + dashboardRepo.getSupervisorUserIdByAshaId(ashaId) + ); + } + } return updated > 0 ? "Success" : "No records"; @@ -574,7 +942,7 @@ public String updateClaimStatus(Integer ashaId, Integer month, Integer year, Boo } } - private void incentiveOfNcdReferal(Integer ashaId, Integer stateId) { + private void incentiveOfNcdReferal(Integer ashaId, Integer stateId, GetBenRequestHandler request) { try { String userName = userRepo.getUserNamedByUserId(ashaId); String groupName = resolveGroupName(stateId); @@ -609,21 +977,23 @@ private void incentiveOfNcdReferal(Integer ashaId, Integer stateId) { } if (hwcReferralEnumeration != null) { - logger.info("hwcReferralEnumeration :" + hwcReferralEnumeration.getDescription()); + logger.info("hwcReferralEnumeration: {}", hwcReferralEnumeration.getDescription()); logger.info("userName={}", userName); - LocalDate now = LocalDate.now(); + LocalDate requestDate = request.getFromDate() + .toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); Long referralCount = benReferDetailsRepo.countMonthlyReferrals( userName, - now.getMonthValue(), - now.getYear()); - - logger.info("referralCount :" + referralCount); + requestDate.getMonthValue(), + requestDate.getYear() + ); + logger.info("referralCount: {}", referralCount); if (referralCount >= 10) { - addHwcReferalAshaIncentiveRecord( hwcReferralEnumeration, ashaId, @@ -691,15 +1061,15 @@ private void checkMonthlyAshaIncentive(Integer ashaId) { 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); + addMonthlyAshaIncentiveRecord(MOBILEBILLREIMB_ACTIVITY, ashaId, userName,false); } if (ADDITIONAL_ASHA_INCENTIVE != null) { - addMonthlyAshaIncentiveRecord(ADDITIONAL_ASHA_INCENTIVE, ashaId, userName); + addMonthlyAshaIncentiveRecord(ADDITIONAL_ASHA_INCENTIVE, ashaId, userName,false); } if (ASHA_MONTHLY_ROUTINE != null) { - addMonthlyAshaIncentiveRecord(ASHA_MONTHLY_ROUTINE, ashaId, userName); + addMonthlyAshaIncentiveRecord(ASHA_MONTHLY_ROUTINE, ashaId, userName,false); } } catch (Exception e) { @@ -717,15 +1087,15 @@ private void checkMonthlyAshaIncentiveForCg(Integer ashaId) { 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); + addMonthlyAshaIncentiveRecord(MONTHLY_HONORARIUM, ashaId, userName,true); } if (MITANIN_REGISTER_5_INFO_FILL != null) { - addMonthlyAshaIncentiveRecord(MITANIN_REGISTER_5_INFO_FILL, ashaId, userName); + addMonthlyAshaIncentiveRecord(MITANIN_REGISTER_5_INFO_FILL, ashaId, userName,false); } if (MITANIN_REGISTER != null) { - addMonthlyAshaIncentiveRecord(MITANIN_REGISTER, ashaId, userName); + addMonthlyAshaIncentiveRecord(MITANIN_REGISTER, ashaId, userName,false); } } catch (Exception e) { @@ -735,7 +1105,7 @@ private void checkMonthlyAshaIncentiveForCg(Integer ashaId) { } - private void addMonthlyAshaIncentiveRecord(IncentiveActivity incentiveActivity, Integer ashaId, String userName) { + private void addMonthlyAshaIncentiveRecord(IncentiveActivity incentiveActivity, Integer ashaId, String userName,Boolean isApproved) { try { Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now()); @@ -764,6 +1134,7 @@ record = new IncentiveActivityRecord(); record.setAshaId(ashaId); record.setIsEligible(true); record.setIsDefaultActivity(true); + record.setIsApproved(isApproved); record.setAmount(Long.valueOf(incentiveActivity.getRate())); recordRepo.save(record); } diff --git a/src/main/java/com/iemr/flw/service/impl/MaternalHealthServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/MaternalHealthServiceImpl.java index 23973862a..62534f071 100644 --- a/src/main/java/com/iemr/flw/service/impl/MaternalHealthServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/MaternalHealthServiceImpl.java @@ -216,8 +216,6 @@ public String saveANCVisit(List ancVisitDTOs, Integer userId) { benVisitDetail.setModifiedBy(it.getUpdatedBy()); benVisitDetail.setLastModDate(it.getUpdatedDate()); benVisitDetail.setProviderServiceMapID(it.getProviderServiceMapID()); - if (benVisitDetail.getCreatedDate() == null) - benVisitDetail.setCreatedDate(new java.sql.Timestamp(System.currentTimeMillis())); logger.info("Saving BenVisitDetail"); @@ -420,8 +418,6 @@ public String savePNCVisit(List pncVisitDTOs) { benVisitDetail.setProcessed("N"); benVisitDetail.setModifiedBy(it.getUpdatedBy()); benVisitDetail.setLastModDate(it.getUpdatedDate()); - if (benVisitDetail.getCreatedDate() == null) - benVisitDetail.setCreatedDate(new java.sql.Timestamp(System.currentTimeMillis())); benVisitDetail = benVisitDetailsRepo.save(benVisitDetail); // Saving Data in AncCare table @@ -443,80 +439,173 @@ public String savePNCVisit(List pncVisitDTOs) { logger.info("PNC visit details saved"); return "no of pnc details saved: " + pncList.size(); } catch (Exception e) { - logger.info("Saving PNC visit details failed with error : " + e.getMessage()); + logger.error("Saving PNC visit details failed", e); + } return null; } @Override @Transactional - public String saveANCVisitQuestions(List dtos, String authorization) throws IEMRException { + public String saveANCVisitQuestions( + List dtos, + String authorization) throws IEMRException { + + if (dtos == null || dtos.isEmpty()) { + throw new IllegalArgumentException("ANC visit data is mandatory"); + } + Integer userId = jwtUtil.extractUserId(authorization); String userName = userRepo.getUserNamedByUserId(userId); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + DateTimeFormatter formatter = DateTimeFormatter + .ofPattern("dd-MM-uuuu") + .withResolverStyle(java.time.format.ResolverStyle.STRICT); + List entities = new ArrayList<>(); for (AncCounsellingCareDTO dto : dtos) { + if (dto == null) { + throw new IllegalArgumentException( + "ANC visit record cannot be null"); + } + + logger.info( + "ANC counselling DTO received: id={}, visitDate={}", + dto.getId(), + dto.getVisitDate() + ); + + if (dto.getId() != null && dto.getId() < 0) { + throw new IllegalArgumentException( + "id must be zero, null, or a positive existing record ID"); + } + + if (dto.getBeneficiaryId() == null + || dto.getBeneficiaryId() <= 0) { + throw new IllegalArgumentException( + "Valid beneficiaryId is mandatory"); + } + if (!StringUtils.hasText(dto.getVisitDate())) { throw new IllegalArgumentException("visitDate is mandatory"); } AncCounsellingCareListDTO fields = dto.getFields(); + if (fields == null) { throw new IllegalArgumentException("fields object is mandatory"); } LocalDate visitDate; try { - visitDate = LocalDate.parse(dto.getVisitDate(), formatter); + visitDate = LocalDate.parse( + dto.getVisitDate().trim(), formatter); } catch (DateTimeParseException e) { - throw new IllegalArgumentException("Invalid visitDate format, expected dd-MM-yyyy"); + throw new IllegalArgumentException( + "Invalid visitDate, expected dd-MM-yyyy", e); } LocalDate homeVisitDate; try { homeVisitDate = StringUtils.hasText(fields.getHomeVisitDate()) - ? LocalDate.parse(fields.getHomeVisitDate(), formatter) - : visitDate; // ✅ fallback + ? LocalDate.parse( + fields.getHomeVisitDate().trim(), formatter) + : visitDate; } catch (DateTimeParseException e) { - throw new IllegalArgumentException("Invalid home_visit_date format, expected dd-MM-yyyy"); + throw new IllegalArgumentException( + "Invalid home_visit_date, expected dd-MM-yyyy", e); + } + + AncCounsellingCare entity; + + if (dto.getId() != null && dto.getId() > 0) { + + // Existing ID: update only. Never create if this ID is missing. + entity = ancCounsellingCareRepo.findById(dto.getId()) + .orElseThrow(() -> new IllegalArgumentException( + "ANC counselling record not found for id: " + + dto.getId())); + + // Prevent updating a different beneficiary's record. + if (!dto.getBeneficiaryId().equals(entity.getBeneficiaryId())) { + throw new IllegalArgumentException( + "beneficiaryId does not match ANC counselling record: " + + dto.getId()); + } + + logger.info( + "Updating ANC counselling record: id={}", + entity.getId() + ); + + } else { + + entity = new AncCounsellingCare(); + entity.setBeneficiaryId(dto.getBeneficiaryId()); + entity.setUserId(userId); + entity.setCreatedBy(userName); + + logger.info( + "Creating ANC counselling record: requestId={}", + dto.getId() + ); + } + + if (entity.getAncVisitId() == null) { + entity.setAncVisitId(0L); } - AncCounsellingCare entity = new AncCounsellingCare(); - entity.setBeneficiaryId(dto.getBeneficiaryId()); - entity.setAncVisitId(0L); entity.setVisitDate(visitDate); entity.setHomeVisitDate(homeVisitDate); - entity.setSelectAll(yesNoToBoolean(fields.getSelectAll())); - entity.setSwelling(yesNoToBoolean(fields.getSwelling())); - entity.setHighBp(yesNoToBoolean(fields.getHighBp())); - entity.setConvulsions(yesNoToBoolean(fields.getConvulsions())); - entity.setAnemia(yesNoToBoolean(fields.getAnemia())); - entity.setReducedFetalMovement(yesNoToBoolean(fields.getReducedFetalMovement())); - entity.setAgeRisk(yesNoToBoolean(fields.getAgeRisk())); - entity.setChildGap(yesNoToBoolean(fields.getChildGap())); - entity.setShortHeight(yesNoToBoolean(fields.getShortHeight())); - entity.setPrePregWeight(yesNoToBoolean(fields.getPrePregWeight())); - entity.setBleeding(yesNoToBoolean(fields.getBleeding())); - entity.setMiscarriageHistory(yesNoToBoolean(fields.getMiscarriageHistory())); - entity.setFourPlusDelivery(yesNoToBoolean(fields.getFourPlusDelivery())); - entity.setFirstDelivery(yesNoToBoolean(fields.getFirstDelivery())); - entity.setTwinPregnancy(yesNoToBoolean(fields.getTwinPregnancy())); - entity.setCSectionHistory(yesNoToBoolean(fields.getCSectionHistory())); - entity.setPreExistingDisease(yesNoToBoolean(fields.getPreExistingDisease())); - entity.setFeverMalaria(yesNoToBoolean(fields.getFeverMalaria())); - entity.setJaundice(yesNoToBoolean(fields.getJaundice())); - entity.setSickleCell(yesNoToBoolean(fields.getSickleCell())); - entity.setProlongedLabor(yesNoToBoolean(fields.getProlongedLabor())); - entity.setMalpresentation(yesNoToBoolean(fields.getMalpresentation())); - - entity.setUserId(userId); - entity.setCreatedBy(userName); - entity.setUpdatedBy(userName); + entity.setSelectAll( + yesNoToBoolean(fields.getSelectAll())); + entity.setSwelling( + yesNoToBoolean(fields.getSwelling())); + entity.setHighBp( + yesNoToBoolean(fields.getHighBp())); + entity.setConvulsions( + yesNoToBoolean(fields.getConvulsions())); + entity.setAnemia( + yesNoToBoolean(fields.getAnemia())); + entity.setReducedFetalMovement( + yesNoToBoolean(fields.getReducedFetalMovement())); + entity.setAgeRisk( + yesNoToBoolean(fields.getAgeRisk())); + entity.setChildGap( + yesNoToBoolean(fields.getChildGap())); + entity.setShortHeight( + yesNoToBoolean(fields.getShortHeight())); + entity.setPrePregWeight( + yesNoToBoolean(fields.getPrePregWeight())); + entity.setBleeding( + yesNoToBoolean(fields.getBleeding())); + entity.setMiscarriageHistory( + yesNoToBoolean(fields.getMiscarriageHistory())); + entity.setFourPlusDelivery( + yesNoToBoolean(fields.getFourPlusDelivery())); + entity.setFirstDelivery( + yesNoToBoolean(fields.getFirstDelivery())); + entity.setTwinPregnancy( + yesNoToBoolean(fields.getTwinPregnancy())); + entity.setCSectionHistory( + yesNoToBoolean(fields.getCSectionHistory())); + entity.setPreExistingDisease( + yesNoToBoolean(fields.getPreExistingDisease())); + entity.setFeverMalaria( + yesNoToBoolean(fields.getFeverMalaria())); + entity.setJaundice( + yesNoToBoolean(fields.getJaundice())); + entity.setSickleCell( + yesNoToBoolean(fields.getSickleCell())); + entity.setProlongedLabor( + yesNoToBoolean(fields.getProlongedLabor())); + entity.setMalpresentation( + yesNoToBoolean(fields.getMalpresentation())); + entity.setUpdatedBy(userName); entities.add(entity); } @@ -539,6 +628,7 @@ public List getANCCounselling(GetBenRequestHandle AncCounsellingCareResponseDTO responseDTO = new AncCounsellingCareResponseDTO(); responseDTO.setFormId("anc_form_001"); + responseDTO.setId(entity.getId()); responseDTO.setBeneficiaryId(entity.getBeneficiaryId()); // Update with actual value responseDTO.setVisitDate(entity.getVisitDate().format(formatter)); // Format visit.getVisitDate() @@ -572,7 +662,7 @@ public List getANCCounselling(GetBenRequestHandle fields.put("sickle_cell", booleanToYesNo(entity.getSickleCell())); fields.put("prolonged_labor", booleanToYesNo(entity.getProlongedLabor())); fields.put("malpresentation", booleanToYesNo(entity.getMalpresentation())); - + fields.put("id", entity.getId()); responseDTO.setFields(fields); responseDTOList.add(responseDTO); @@ -588,14 +678,13 @@ private String booleanToYesNo(Boolean value) { private Boolean yesNoToBoolean(String value) { - return "Yes".equalsIgnoreCase(value); + return value != null && "Yes".equalsIgnoreCase(value.trim()); } private void checkAndAddAntaraIncentive(PNCVisit ect) { Integer userId = userRepo.getUserIdByName(ect.getCreatedBy()); Integer stateId = userRepo.getUserRole(userId).get(0).getStateId(); - logger.info("ContraceptionMethod:" + ect.getContraceptionMethod()); // logic for assam if (stateId.equals(StateCode.AM.getStateCode())) { @@ -664,18 +753,24 @@ private void checkAndAddAntaraIncentive(PNCVisit ect) { if (PPIUCDActivityCH != null) { - if (ect.getContraceptionMethod().equals("POST PARTUM IUCD (PPIUCD)")) { - addIncenticeRecord(ect, userId, PPIUCDActivityCH); + if(ect.getContraceptionMethod()!=null){ + if (ect.getContraceptionMethod().equals("POST PARTUM IUCD (PPIUCD)")) { + addIncenticeRecord(ect, userId, PPIUCDActivityCH); + } } + } if (femaleSterilizationActivityCH != null) { - if (ect.getContraceptionMethod().equals("FEMALE STERILIZATION")) { - addIncenticeRecord(ect, userId, femaleSterilizationActivityCH); + if(ect.getAnyContraceptionMethod()!=null){ + if (ect.getContraceptionMethod().equals("FEMALE STERILIZATION")) { + addIncenticeRecord(ect, userId, femaleSterilizationActivityCH); + } } + } @@ -689,41 +784,54 @@ private void checkAndAddAntaraIncentive(PNCVisit ect) { } - if (ect.getMotherDangerSign() != null - && !ect.getMotherDangerSign().isEmpty() - && ect.getPncPeriod() == 42) { - IncentiveActivity highRiskPostpartumCareActivityCH = - incentivesRepo.findIncentiveMasterByNameAndGroup("HIGH_RISK_POSTPARTUM_CARE", GroupName.ACTIVITY.getDisplayName()); - if (highRiskPostpartumCareActivityCH != null) { - addIncenticeRecord(ect, userId, highRiskPostpartumCareActivityCH); - - } - - } - if (ect.getPncPeriod() == 42) { - IncentiveActivity highRiskPostpartumHealthCareActivityCH = - incentivesRepo.findIncentiveMasterByNameAndGroup("HIGH_RISK_POSTPARTUM_HEALTH_CHECK", GroupName.ACTIVITY.getDisplayName()); - - if (highRiskPostpartumHealthCareActivityCH != null) { - addIncenticeRecord(ect, userId, highRiskPostpartumHealthCareActivityCH); - - } - - - } +// if (ect.getMotherDangerSign() != null +// && !ect.getMotherDangerSign().isEmpty() +// && ect.getPncPeriod() != null +// && ect.getPncPeriod() >= 1 +// && ect.getPncPeriod() <= 42) { +// IncentiveActivity highRiskPostpartumCareActivityCH = +// incentivesRepo.findIncentiveMasterByNameAndGroup("HIGH_RISK_POSTPARTUM_CARE", GroupName.ACTIVITY.getDisplayName()); +// +// if (highRiskPostpartumCareActivityCH != null ) { +// addIncenticeRecord(ect, userId, highRiskPostpartumCareActivityCH); +// } +// +// } +// if (ect.getPncPeriod() == 42 +// && !ect.getMotherDeath() +// && ancVisitRepo.findByBenId(ect.getBenId()) +// .stream() +// .filter(ancVisit -> ancVisit.getAncVisit() != null +// && ancVisit.getAncVisit() >= 1 +// && ancVisit.getAncVisit() <= 7) +// .anyMatch(ancVisit -> +// Boolean.TRUE.equals(ancVisit.getIsHrpConfirmed()))) { +// +// IncentiveActivity highRiskPostpartumHealthCareActivityCH = +// incentivesRepo.findIncentiveMasterByNameAndGroup( +// "HIGH_RISK_POSTPARTUM_HEALTH_CHECK", +// GroupName.ACTIVITY.getDisplayName()); +// +// if (highRiskPostpartumHealthCareActivityCH != null) { +// addIncenticeRecord(ect, userId, +// highRiskPostpartumHealthCareActivityCH); +// } +// } if (ect.getPncPeriod() == 1 || ect.getPncPeriod() == 3 || ect.getPncPeriod() == 7) { + if(ect.getContraceptionMethod()!=null){ + if ((ect.getContraceptionMethod().equals("POST PARTUM STERILIZATION (PPS)") + || ect.getContraceptionMethod().equals("MiniLap")) + && ect.getSterilisationDate() != null) { + IncentiveActivity ppsActivityCH = + incentivesRepo.findIncentiveMasterByNameAndGroup("FP_PPS", GroupName.ACTIVITY.getDisplayName()); + if (ppsActivityCH != null) { - if ((ect.getContraceptionMethod().equals("POST PARTUM STERILIZATION (PPS)") - || ect.getContraceptionMethod().equals("MiniLap")) - && ect.getSterilisationDate() != null) { - IncentiveActivity ppsActivityCH = - incentivesRepo.findIncentiveMasterByNameAndGroup("FP_PPS", GroupName.ACTIVITY.getDisplayName()); - if (ppsActivityCH != null) { + addIncenticeRecord(ect, userId, ppsActivityCH); - addIncenticeRecord(ect, userId, ppsActivityCH); + } + } + } - } - } } } @@ -732,6 +840,7 @@ private void checkAndAddAntaraIncentive(PNCVisit ect) { } + @Transactional private void addIncenticeRecord(PNCVisit ect, Integer userId, IncentiveActivity antaraActivity) { @@ -854,7 +963,7 @@ private void checkAndAddIncentives(List ancList, Integer userId) { if (ancFullActivityCH != null && ancVisit.getAncVisit() != null && ancVisit.getAncVisit() == 4) { - recordAncRelatedIncentive(ancFullActivityCH, ancVisit); + recordFullAncIncentive(ancFullActivityCH, ancVisit); } @@ -876,12 +985,12 @@ private void recordAncRelatedIncentive(IncentiveActivity incentiveActivity, ANCV if (record == null) { record = new IncentiveActivityRecord(); record.setActivityId(incentiveActivity.getId()); - record.setCreatedDate(ancVisit.getCreatedDate()); + record.setCreatedDate(ancVisit.getAncDate()); record.setCreatedBy(ancVisit.getCreatedBy()); - record.setUpdatedDate(ancVisit.getCreatedDate()); + record.setUpdatedDate(ancVisit.getAncDate()); record.setUpdatedBy(ancVisit.getCreatedBy()); - record.setStartDate(ancVisit.getCreatedDate()); - record.setEndDate(ancVisit.getCreatedDate()); + record.setStartDate(ancVisit.getAncDate()); + record.setEndDate(ancVisit.getAncDate()); record.setBenId(ancVisit.getBenId()); record.setAshaId(userId); record.setAmount(Long.valueOf(incentiveActivity.getRate())); diff --git a/src/main/java/com/iemr/flw/service/impl/MdaFormSubmissionServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/MdaFormSubmissionServiceImpl.java index e7b0fe4eb..b25bfbab3 100644 --- a/src/main/java/com/iemr/flw/service/impl/MdaFormSubmissionServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/MdaFormSubmissionServiceImpl.java @@ -6,6 +6,7 @@ import com.iemr.flw.dto.iemr.MdaFormSubmissionResponse; import com.iemr.flw.repo.iemr.MdaFormSubmissionRepository; +import com.iemr.flw.repo.iemr.UserServiceRoleRepo; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -24,6 +25,9 @@ public class MdaFormSubmissionServiceImpl implements MdaFormSubmissionService { @Autowired private final MdaFormSubmissionRepository repository; + @Autowired + private UserServiceRoleRepo userServiceRoleRepo; + @Override public String saveFormData(List requests) { try { @@ -45,6 +49,7 @@ public String saveFormData(List requests) { .mdaDistributionDate(getMdaDistributionDate) .isMedicineDistributed(req.getFields().getIs_medicine_distributed()) .createdBy(req.getUserName()) + .ashaId(userServiceRoleRepo.getUserIdByName(req.getUserName())) .modifiedBy(req.getUserName()) .build(); entities.add(data); diff --git a/src/main/java/com/iemr/flw/service/impl/NotificationSchedulerService.java b/src/main/java/com/iemr/flw/service/impl/NotificationSchedulerService.java index 21d0da458..39314c8ff 100644 --- a/src/main/java/com/iemr/flw/service/impl/NotificationSchedulerService.java +++ b/src/main/java/com/iemr/flw/service/impl/NotificationSchedulerService.java @@ -14,8 +14,6 @@ public class NotificationSchedulerService { final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); - @Autowired - private NotificationService notificationService; @Autowired EmployeeMasterInter employeeMasterInter; @@ -43,13 +41,6 @@ public void triggerAncRemindersForAllAsha() { String topic = "All"; // or some user/topic identifier String title = "ANC Reminder"; - notificationService.sendNotification( - appType, - topic, - title, - body, - redirectPath - ); } diff --git a/src/main/java/com/iemr/flw/service/impl/NotificationService.java b/src/main/java/com/iemr/flw/service/impl/NotificationService.java deleted file mode 100644 index 838ff3ac8..000000000 --- a/src/main/java/com/iemr/flw/service/impl/NotificationService.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.iemr.flw.service.impl; - -import com.google.gson.Gson; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.util.HashMap; -import java.util.Map; - -@Service -public class NotificationService { - final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); - - - private String NOTIFICATION_URL = "https://uatamrit.piramalswasthya.org/common-api/firebaseNotification/sendNotification"; - - public String sendNotification(String appType, String topic, String title, String body, String redirect) { - String authHeader = null; - String jwtToken = null; - - // Check if we have HTTP request context - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - if (attributes != null) { - HttpServletRequest httpServletRequest = attributes.getRequest(); - - authHeader = httpServletRequest.getHeader("Authorization"); - - if (httpServletRequest.getCookies() != null) { - for (Cookie cookie : httpServletRequest.getCookies()) { - if ("Jwttoken".equals(cookie.getName())) { - jwtToken = cookie.getValue(); - } - } - } - } - - // If no request context, set default (for scheduler/startup use) - if (authHeader == null) { - authHeader = "Bearer DEFAULT_TOKEN_IF_REQUIRED"; // or leave null if API allows - } - if (jwtToken == null) { - jwtToken = "DEFAULT_JWT_IF_REQUIRED"; - } - - RestTemplate restTemplate = new RestTemplate(); - MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add("Content-Type", "application/json"); -// headers.add("AUTHORIZATION", authHeader); -// headers.add("Cookie", "Jwttoken=" + jwtToken); - - Map requestBody = new HashMap<>(); - requestBody.put("appType", appType); - requestBody.put("token", topic); - requestBody.put("title", title); - requestBody.put("body", body); - - Map dataMap = new HashMap<>(); - dataMap.put("NotificationTypeId", redirect); - requestBody.put("data", dataMap); - - String jsonRequest = new Gson().toJson(requestBody); - - HttpEntity request = new HttpEntity<>(jsonRequest, headers); - - ResponseEntity response = restTemplate.exchange(NOTIFICATION_URL, HttpMethod.POST, request, String.class); - - return response.getBody(); - } -} diff --git a/src/main/java/com/iemr/flw/service/impl/NotificationServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/NotificationServiceImpl.java new file mode 100644 index 000000000..8a157cb2f --- /dev/null +++ b/src/main/java/com/iemr/flw/service/impl/NotificationServiceImpl.java @@ -0,0 +1,222 @@ +package com.iemr.flw.service.impl; + +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.Notification; +import com.iemr.flw.domain.iemr.UserFcmTokenData; +import com.iemr.flw.dto.iemr.NotificationListDTO; +import com.iemr.flw.repo.iemr.NotificationRepository; +import com.iemr.flw.repo.iemr.UserFcmTokenRepo; +import com.iemr.flw.service.NotificationService; +import com.iemr.flw.utils.JwtUtil; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Service +public class NotificationServiceImpl implements NotificationService { + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Value("${common-api-base-url}") + private String commonApiBaseUrl; + @Autowired + private JwtUtil jwtUtil; + + @Autowired + private NotificationRepository notificationRepository; + + @Autowired + private UserFcmTokenRepo userFcmTokenRepo; + + + private String NOTIFICATION_URL = "/firebaseNotification/sendNotification"; + + public String sendNotification(String appType, String topic, String title, + String body, String redirect, + String notificationType, Integer receiverId) { + + logger.info("Initiating notification. appType={}, receiverId={}, type={} , commonApiBaseUrl={}", + appType, receiverId, notificationType,commonApiBaseUrl); + + try { + String authHeader = null; + ServletRequestAttributes attributes = + (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + + if (attributes != null) { + HttpServletRequest request = attributes.getRequest(); + authHeader = request.getHeader("jwtToken"); + } + + Integer senderId = jwtUtil.extractUserId(authHeader); + + logger.debug("Sender Id: {}", senderId); + + + UserFcmTokenData tokenData = userFcmTokenRepo.findByUserId(receiverId); + + if (tokenData == null) { + logger.warn("FCM token not found for receiverId={}", receiverId); + return "FCM token not found."; + } + + String token = tokenData.getToken(); + + + + RestTemplate restTemplate = new RestTemplate(); + + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add("Content-Type", "application/json"); + headers.add("jwtToken", authHeader); + + Map requestBody = new HashMap<>(); + requestBody.put("appType", appType); + requestBody.put("token", token); + requestBody.put("title", title); + requestBody.put("body", body); + + Map dataMap = new HashMap<>(); + dataMap.put("notification_id", notificationType); + dataMap.put("notification_type", notificationType); + dataMap.put("nav_id", redirect); + dataMap.put("sender_user_id", senderId); + dataMap.put("receiver_user_id", receiverId); + dataMap.put("priority", "HIGH"); + dataMap.put("redirect", redirect); + + requestBody.put("data", dataMap); + + String jsonRequest = new Gson().toJson(requestBody); + + logger.debug("Notification request payload: {}", NOTIFICATION_URL); + HttpEntity request = new HttpEntity<>(jsonRequest, headers); + + ResponseEntity response = restTemplate.exchange( + commonApiBaseUrl+NOTIFICATION_URL, + HttpMethod.POST, + request, + String.class); + + logger.info("Notification API response. Status={}, Body={}", + response.getStatusCode(), + response.getBody()); + + saveNotificationRecord(appType, senderId, receiverId, title, body, + notificationType, "INCENTIVE_APPROVAL", redirect, "HIGH"); + + logger.info("Notification record saved successfully. receiverId={}", receiverId); + + return response.getBody(); + + } catch (HttpClientErrorException | HttpServerErrorException ex) { + + logger.error("Notification API failed. Status={}, Response={}", + ex.getStatusCode(), + ex.getResponseBodyAsString(), + ex); + + throw ex; + + } catch (Exception ex) { + + logger.error("Unexpected error while sending notification. receiverId={}, type={}", + receiverId, + notificationType, + ex); + + throw new RuntimeException("Failed to send notification.", ex); + } + } + + + private void saveNotificationRecord(String appType, Integer senderUserId, Integer receiverUserId, + String title, String body, String notificationType, + String navId, String redirect, String priority) { + try { + Notification notification = new Notification(); + notification.setAppType(appType); + notification.setSenderUserId(senderUserId); + notification.setReceiverUserId(receiverUserId); + notification.setTitle(title); + notification.setBody(body); + notification.setNotificationType(notificationType); + notification.setNavId(navId); + notification.setRedirect(redirect); + notification.setPriority(priority); + notification.setRead(false); + notificationRepository.save(notification); + } catch (Exception e) { + logger.error("Failed to persist notification record for receiver {}", receiverUserId, e); + } + } + + /** Paged list of a user's notifications, newest first, with unread count attached. */ + public NotificationListDTO getNotifications(Integer receiverUserId) { + + int page = 0; // First page + int size = 20; // Records per page + + Pageable pageable = PageRequest.of( + page, + size, + Sort.by(Sort.Direction.DESC, "createdDate") + ); + + Page result = + notificationRepository.findByReceiverUserIdOrderByCreatedDateDesc( + receiverUserId, + pageable + ); + + long unreadCount = + notificationRepository.countByReceiverUserIdAndIsReadFalse(receiverUserId); + + return new NotificationListDTO( + result.getContent(), + unreadCount, + result.getTotalElements(), + result.getNumber(), + result.getSize() + ); + } + + /** + * Lightweight call for a badge — no list, just the count. + */ + public Long getUnreadCount(Integer receiverUserId) { + return notificationRepository.countByReceiverUserIdAndIsReadFalse(receiverUserId); + } + + /** Marks a single notification read. Returns true if a row was updated. */ + public boolean markAsRead(Long notificationId) { + int updated = notificationRepository.markAsRead(notificationId, new Date()); + return updated > 0; + } + + /** Marks all of a user's notifications read. Returns the number updated. */ + public int markAllAsRead(Integer receiverUserId) { + return notificationRepository.markAllAsRead(receiverUserId, new Date()); + } +} diff --git a/src/main/java/com/iemr/flw/service/impl/StopTBServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/StopTBServiceImpl.java index 891827b5c..3c00b1973 100644 --- a/src/main/java/com/iemr/flw/service/impl/StopTBServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/StopTBServiceImpl.java @@ -30,6 +30,7 @@ import com.iemr.flw.repo.iemr.StopTBGeneralOpdRepo; import com.iemr.flw.repo.iemr.TBScreeningRepo; import com.iemr.flw.service.CampConfigService; +import com.iemr.flw.service.StopTBPrescriptionService; import com.iemr.flw.service.StopTBService; import com.iemr.flw.service.TBStopVisitService; import org.slf4j.Logger; @@ -51,6 +52,9 @@ public class StopTBServiceImpl implements StopTBService { @Autowired private CampConfigService campConfigService; + @Autowired + private StopTBPrescriptionService stopTBPrescriptionService; + @Autowired private BenFlowStatusRepo benFlowStatusRepo; @@ -367,18 +371,48 @@ private Map screeningToMap(TBScreening s) { // ── Nurse: General OPD ──────────────────────────────────────────────────── + // Whole list is one transaction: any failure (incl. insufficient stock) rolls back every record. @Override - @Transactional + @Transactional(rollbackFor = Exception.class) public List> saveGeneralOpd(List> dataList) throws Exception { List> results = new ArrayList<>(); - Integer vanID = campConfigService.getVanID(); Integer parkingPlaceID = campConfigService.getParkingPlaceID(); for (Map data : dataList) { Long beneficiaryRegID = getLong(data, "beneficiaryRegID"); if (beneficiaryRegID == null) throw new Exception("beneficiaryRegID is required"); + Integer vanID = campConfigService.resolveVanID(getInt(data, "vanID")); Integer providerServiceMapID = getInt(data, "providerServiceMapID"); String createdBy = getString(data, "createdBy"); + + // New payload (chiefComplaints[] / prescription.drugs[]): AMRIT standard tables only, with dispensing. + // tb_stoptb_general_opd is not written; getAll and the Nikshay export read the standard tables. + if (data.get("chiefComplaints") instanceof List || data.get("prescription") instanceof Map) { + if (providerServiceMapID == null) throw new Exception("providerServiceMapID is required"); + if (createdBy == null || createdBy.isBlank()) throw new Exception("createdBy is required"); + String submissionId = getString(data, "submissionId"); + Map previous = stopTBPrescriptionService.findPreviousSubmission(beneficiaryRegID, submissionId); + if (previous != null) { + results.add(previous); + continue; + } + BenVisitDetail visit = tbStopVisitService.getOrCreateVisitForToday(beneficiaryRegID, providerServiceMapID, + createdBy, vanID, parkingPlaceID); + Map result = new LinkedHashMap<>(); + result.put("beneficiaryRegID", beneficiaryRegID); + result.put("visitCode", visit.getVisitCode()); + stopTBPrescriptionService.saveChiefComplaints(toMapList(data.get("chiefComplaints")), beneficiaryRegID, + visit, createdBy, vanID, parkingPlaceID); + if (data.get("prescription") instanceof Map) { + result.putAll(stopTBPrescriptionService.prescribeAndDispense( + (Map) data.get("prescription"), beneficiaryRegID, visit, submissionId, + createdBy, vanID, parkingPlaceID)); + } + results.add(result); + continue; + } + + // Old app payload (free-text chiefComplaint / medication): unchanged behaviour. BenVisitDetail visit = tbStopVisitService.getOrCreateVisitForToday(beneficiaryRegID, providerServiceMapID, createdBy, vanID, parkingPlaceID); @@ -416,13 +450,30 @@ public List> saveGeneralOpd(List> dataLi return results; } + @SuppressWarnings("unchecked") + private List> toMapList(Object v) { + return v instanceof List ? (List>) v : null; + } + + // Standard tables first (visits saved with the new payload); for older visits, which exist only in + // tb_stoptb_general_opd, fall back to that table. Same response fields either way. @Override public Map getAllGeneralOpd(Integer providerServiceMapID, Integer villageID) throws Exception { + List> items = stopTBPrescriptionService.getStandardOpdRecords(providerServiceMapID, villageID); + Set standardVisitCodes = new HashSet<>(); + for (Map item : items) standardVisitCodes.add((Long) item.get("visitCode")); + List list = (villageID != null) ? generalOpdRepo.findAllByProviderServiceMapIDAndVillageID(providerServiceMapID, villageID) : generalOpdRepo.findAllByProviderServiceMapID(providerServiceMapID); - List> items = new ArrayList<>(); - for (StopTBGeneralOpd o : list) items.add(opdToMap(o)); + for (StopTBGeneralOpd o : list) { + if (o.getVisitCode() == null || !standardVisitCodes.contains(o.getVisitCode())) items.add(opdToMap(o)); + } + items.sort((a, b) -> { + Date da = (Date) a.get("createdDate"), db = (Date) b.get("createdDate"); + if (da == null || db == null) return da == null ? (db == null ? 0 : 1) : -1; + return db.compareTo(da); + }); Map result = new HashMap<>(); result.put("data", items); result.put("count", items.size()); @@ -440,6 +491,7 @@ private Map opdToMap(StopTBGeneralOpd o) { m.put("dosage", o.getDosage()); m.put("frequency", o.getFrequency()); m.put("duration", o.getDuration()); + m.put("drugs", Collections.emptyList()); // old free-text records have no per-drug details m.put("notes", o.getNotes()); m.put("createdBy", o.getCreatedBy()); m.put("createdDate", o.getCreatedDate()); @@ -527,6 +579,61 @@ public Map getAllDiagnostics(Integer providerServiceMapID, Integ return result; } + // ── By id, for the changes feed ─────────────────────────────────────────── + + @Override + public Map> getNurseTBScreeningsByIds(Collection ids) { + Map> out = new LinkedHashMap<>(); + for (TBScreening s : tbScreeningRepo.findAllById(ids)) { + if (!Boolean.TRUE.equals(s.getDeleted())) out.put(s.getId(), screeningToMap(s)); + } + return out; + } + + @Override + public Map> getGeneralExaminationsByIds(Collection ids) { + Map> out = new LinkedHashMap<>(); + for (StopTBGeneralExamination e : generalExaminationRepo.findAllById(ids)) { + if (!Boolean.TRUE.equals(e.getDeleted())) out.put(e.getId(), examToMap(e)); + } + return out; + } + + @Override + public Map> getDiagnosticsByIds(Collection ids) { + Map> out = new LinkedHashMap<>(); + for (StopTBDiagnostics d : diagnosticsRepo.findAllById(ids)) { + if (!Boolean.TRUE.equals(d.getDeleted())) out.put(d.getId(), diagnosticsToMap(d)); + } + return out; + } + + @Override + public Map> getGeneralOpdByVisitIds(Collection benVisitIds) { + Map> out = new LinkedHashMap<>(); + for (Map m : stopTBPrescriptionService.getStandardOpdRecordsByVisitIds(benVisitIds)) { + out.put((Long) m.get("id"), m); + } + return out; + } + + @Override + public Map> getLegacyGeneralOpdByIds(Collection ids) { + List rows = new ArrayList<>(); + Set visitCodes = new HashSet<>(); + for (StopTBGeneralOpd o : generalOpdRepo.findAllById(ids)) { + if (Boolean.TRUE.equals(o.getDeleted())) continue; + rows.add(o); + if (o.getVisitCode() != null) visitCodes.add(o.getVisitCode()); + } + Set standard = stopTBPrescriptionService.visitCodesWithStandardOpd(visitCodes); + Map> out = new LinkedHashMap<>(); + for (StopTBGeneralOpd o : rows) { + if (o.getVisitCode() == null || !standard.contains(o.getVisitCode())) out.put(o.getId(), opdToMap(o)); + } + return out; + } + private Map diagnosticsToMap(StopTBDiagnostics d) { Map m = new LinkedHashMap<>(); m.put("id", d.getId()); diff --git a/src/main/java/com/iemr/flw/service/impl/StopTBSyncServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/StopTBSyncServiceImpl.java new file mode 100644 index 000000000..4b144d60a --- /dev/null +++ b/src/main/java/com/iemr/flw/service/impl/StopTBSyncServiceImpl.java @@ -0,0 +1,483 @@ +package com.iemr.flw.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParser; +import com.iemr.flw.service.BeneficiaryService; +import com.iemr.flw.service.DiagnosticOrderService; +import com.iemr.flw.service.DynamicFormResponseService; +import com.iemr.flw.service.StopTBService; +import com.iemr.flw.service.StopTBSyncService; +import com.iemr.flw.service.TBConfirmedCaseService; +import com.iemr.flw.service.TBSuspectedService; +import com.iemr.flw.service.sync.StopTBSyncType; +import com.iemr.flw.service.sync.SyncToken; +import com.iemr.flw.service.sync.UserSyncScope; +import com.iemr.flw.repo.iemr.UserServiceRoleRepo; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * STOP-TB changes feed. AMRIT-DB V132 / V32 install t_sync_change_log and its triggers on every + * server; tablets call the feed on their camp hub (or UAT central when testing). + * + * The tablet keeps one opaque token. With none it gets a first full download in pages (snapshot); + * after that, each call returns only the records with change-log lines after its token, in their + * current state, plus the next token. Village scope is resolved here through i_ben_flow_outreach, + * the same way today's getAll endpoints do. + * + * @author Piramal Swasthya + */ +@Service +public class StopTBSyncServiceImpl implements StopTBSyncService { + + private final Logger logger = LoggerFactory.getLogger(StopTBSyncServiceImpl.class); + + static final int DEFAULT_LIMIT = 200; + static final int MAX_LIMIT = 500; + /** + * Beneficiary records are the expensive ones (several identity lookups and a FHIR call each), so a + * page carries at most this many; the token resumes from the last one sent. + */ + static final int MAX_BENEFICIARIES_PER_PAGE = 25; + /** Margin under the oldest open transaction (see safePoint); lines younger than this wait. */ + static final int SAFE_LAG_SECONDS = 5; + /** The hub keeps 30 days of log; a token older than this may point at cleaned-up lines. */ + static final long MAX_TOKEN_AGE_SECONDS = 29L * 24 * 3600; + private static final long SUPPORT_CHECK_TTL_MS = 5 * 60 * 1000L; + private static final String FLOW_TABLE = "db_iemr.i_ben_flow_outreach"; + + // Same date formats the existing getAll endpoints use for these records. + private final Gson stopTbGson = new GsonBuilder().serializeNulls().create(); + + @PersistenceContext(unitName = "db_iemr") + private EntityManager em; + + @Autowired + private BeneficiaryService beneficiaryService; + @Autowired + private StopTBService stopTBService; + @Autowired + private TBSuspectedService tbSuspectedService; + @Autowired + private TBConfirmedCaseService tbConfirmedCaseService; + @Autowired + private DynamicFormResponseService dynamicFormResponseService; + @Autowired + private DiagnosticOrderService diagnosticOrderService; + @Autowired + private ObjectMapper objectMapper; + + private static final long USER_SCOPE_TTL_MS = 5 * 60 * 1000L; + private final Map> userScopes = new java.util.concurrent.ConcurrentHashMap<>(); + + @Autowired + private UserServiceRoleRepo userServiceRoleRepo; + + private volatile boolean innodbTrxWarned; + private volatile Boolean supported; + private volatile long supportCheckedAt; + + @Override + public Map getChanges(Integer userId, String token, Integer providerServiceMapID, + List villageIds, List types, Integer limit, + String authorisation) { + if (!isSupported()) { + Map out = new LinkedHashMap<>(); + out.put("supported", false); + return out; + } + if (userId == null) throw new IllegalArgumentException("A valid JwtToken is required"); + if (providerServiceMapID == null) throw new IllegalArgumentException("providerServiceMapID is required"); + // Only the user's own service and villages; none asked for means all of them. + List villages = userScope(userId).resolve(providerServiceMapID, villageIds); + int pageSize = limit == null || limit <= 0 ? DEFAULT_LIMIT : Math.min(limit, MAX_LIMIT); + + String serverId = serverId(); + SyncToken parsed = SyncToken.parse(token); + long now = System.currentTimeMillis() / 1000; + boolean usable = parsed != null && serverId.equals(parsed.serverId()) + && (parsed.kind() == SyncToken.Kind.SNAPSHOT || now - parsed.issuedAt() <= MAX_TOKEN_AGE_SECONDS); + + Scope scope = new Scope(providerServiceMapID, villages, requestedTypes(types)); + // A token covers only the types and villages it was issued for; anything else needs a snapshot. + usable = usable && parsed.typesMask() == scope.typesMask() && parsed.scopeHash() == scope.scopeHash(); + if (!usable) { + if (parsed != null) logger.info("Sync token {} not usable here, starting a snapshot", parsed); + return snapshotPage(serverId, SyncToken.snapshot(serverId, safePoint(), 0, 0, scope.typesMask(), scope.scopeHash()), + scope, pageSize, authorisation); + } + return parsed.kind() == SyncToken.Kind.SNAPSHOT + ? snapshotPage(serverId, parsed, scope, pageSize, authorisation) + : deltaPage(serverId, parsed.seq(), scope, pageSize, authorisation); + } + + // ── Snapshot: everything in scope, type by type, keyset-paged ───────────── + + private Map snapshotPage(String serverId, SyncToken at, Scope scope, int pageSize, + String authorisation) { + StopTBSyncType[] types = StopTBSyncType.values(); + int typeIndex = at.typeIndex(); + long after = at.afterKey(); + List> items = new ArrayList<>(); + + while (items.size() < pageSize && typeIndex < types.length) { + StopTBSyncType type = types[typeIndex]; + if (!scope.types.contains(type)) { + typeIndex++; + after = 0; + continue; + } + int want = pageSize - items.size(); + if (type == StopTBSyncType.BENEFICIARY) want = Math.min(want, MAX_BENEFICIARIES_PER_PAGE); + List keys = snapshotKeys(type, scope, after, want); + Map records = build(type, keys, authorisation); + for (Long key : keys) { + String data = records.get(key); + if (data != null) items.add(item(type, "upsert", key, at.seq(), data)); + } + if (keys.size() < want) { + typeIndex++; + after = 0; + } else { + after = keys.get(keys.size() - 1); + break; + } + } + + boolean done = typeIndex >= types.length; + SyncToken next = done + ? SyncToken.delta(serverId, at.seq(), System.currentTimeMillis() / 1000, scope.typesMask(), scope.scopeHash()) + : SyncToken.snapshot(serverId, at.seq(), typeIndex, after, scope.typesMask(), scope.scopeHash()); + return page(serverId, true, items, next, !done); + } + + @SuppressWarnings("unchecked") + private List snapshotKeys(StopTBSyncType type, Scope scope, long after, int limit) { + List rows = em.createNativeQuery(type.snapshotKeySql()) + .setParameter("psm", scope.psm) + .setParameter("villages", scope.villages) + .setParameter("after", after) + .setParameter("limit", limit) + .getResultList(); + List keys = new ArrayList<>(rows.size()); + for (Object r : rows) keys.add(((Number) r).longValue()); + return keys; + } + + // ── Delta: records with log lines after the token ───────────────────────── + + @SuppressWarnings("unchecked") + private Map deltaPage(String serverId, long from, Scope scope, int pageSize, + String authorisation) { + long safe = Math.max(safePoint(), from); + // One row per record: its latest line in range, and that line's op. Deduplicates a record + // changed many times since the token. + List rows = em.createNativeQuery( + "SELECT l.entity, l.row_pk, l.seq, l.op, g.scoped FROM db_iemr.t_sync_change_log l JOIN (" + + " SELECT c.entity, c.row_pk, MAX(c.seq) AS last_seq, MAX(c.op = 'S') AS scoped " + + " FROM db_iemr.t_sync_change_log c " + + " WHERE c.seq > :from AND c.seq <= :safe AND c.entity IN (:entities) AND (" + + " (c.ben_reg_id IS NOT NULL AND EXISTS (SELECT 1 FROM db_iemr.i_ben_flow_outreach f " + + " WHERE f.beneficiary_reg_id = c.ben_reg_id AND f.providerServiceMapID = :psm AND f.villageID IN (:villages)))" + + " OR (c.beneficiary_id IS NOT NULL AND EXISTS (SELECT 1 FROM db_iemr.i_ben_flow_outreach f " + + " WHERE f.beneficiary_id = c.beneficiary_id AND f.providerServiceMapID = :psm AND f.villageID IN (:villages))))" + + " GROUP BY c.entity, c.row_pk ORDER BY last_seq LIMIT :limit" + + ") g ON l.seq = g.last_seq ORDER BY l.seq") + .setParameter("from", from) + .setParameter("safe", safe) + .setParameter("entities", scope.logEntities()) + .setParameter("psm", scope.psm) + .setParameter("villages", scope.villages) + .setParameter("limit", pageSize) + .getResultList(); + boolean full = rows.size() == pageSize; + + // Keep beneficiary records per page bounded: stop before the one over the cap. The token then + // resumes from the last row kept, so nothing after it is skipped. + int beneficiaries = 0; + for (int i = 0; i < rows.size(); i++) { + if (!StopTBSyncType.BENEFICIARY.logEntity().equals(rows.get(i)[0])) continue; + if (++beneficiaries > MAX_BENEFICIARIES_PER_PAGE) { + rows = new ArrayList<>(rows.subList(0, i)); + full = true; + break; + } + } + + // Build each type's records in one batch, then emit in log order. + Map> keysByType = new EnumMap<>(StopTBSyncType.class); + for (Object[] r : rows) { + StopTBSyncType type = StopTBSyncType.fromLogEntity((String) r[0]); + if (type != null) keysByType.computeIfAbsent(type, t -> new ArrayList<>()).add(((Number) r[1]).longValue()); + } + Map> built = new EnumMap<>(StopTBSyncType.class); + keysByType.forEach((type, keys) -> built.put(type, build(type, keys, authorisation))); + + List> items = new ArrayList<>(); + // Beneficiaries that came into scope (op 'S': new flow row, or a village / service change): + // their records logged before that never passed the scope check, so send them now. + List scopedRegIds = new ArrayList<>(); + Map scopedSeq = new HashMap<>(); + for (Object[] r : rows) { + if (StopTBSyncType.BENEFICIARY.logEntity().equals(r[0]) && r[4] != null && ((Number) r[4]).intValue() == 1) { + long regId = ((Number) r[1]).longValue(); + scopedRegIds.add(regId); + scopedSeq.put(regId, ((Number) r[2]).longValue()); + } + } + long lastSeq = from; + for (Object[] r : rows) { + lastSeq = ((Number) r[2]).longValue(); + StopTBSyncType type = StopTBSyncType.fromLogEntity((String) r[0]); + if (type == null) continue; + long key = ((Number) r[1]).longValue(); + String data = built.get(type).get(key); + String op = r[3] == null ? "U" : r[3].toString(); + if (data != null && !"D".equals(op)) { + items.add(item(type, "upsert", key, lastSeq, data)); + } else if ("D".equals(op) || type != StopTBSyncType.BENEFICIARY) { + // Gone, soft-deleted or no longer a record the app keeps (e.g. an OPD visit with no + // complaint or drug). A beneficiary that just failed to build is never deleted on the + // tablet; only a deleted mapping removes it. + items.add(item(type, "delete", key, lastSeq, null)); + } + } + + if (!scopedRegIds.isEmpty()) { + long version = scopedSeq.values().stream().mapToLong(Long::longValue).max().orElse(lastSeq); + for (StopTBSyncType type : scope.types) { + if (type == StopTBSyncType.BENEFICIARY) continue; + List keys = keysForBeneficiaries(type, scope, scopedRegIds); + List alreadySent = keysByType.getOrDefault(type, List.of()); + keys.removeAll(alreadySent); + if (keys.isEmpty()) continue; + Map records = build(type, keys, authorisation); + for (Long key : keys) { + String data = records.get(key); + if (data != null) items.add(item(type, "upsert", key, version, data)); + } + } + } + + boolean hasMore = full; + long nextSeq = hasMore ? lastSeq : safe; + return page(serverId, false, items, + SyncToken.delta(serverId, nextSeq, System.currentTimeMillis() / 1000, scope.typesMask(), scope.scopeHash()), hasMore); + } + + @SuppressWarnings("unchecked") + private List keysForBeneficiaries(StopTBSyncType type, Scope scope, List regIds) { + jakarta.persistence.Query q = em.createNativeQuery(type.keysForBeneficiariesSql()).setParameter("regIds", regIds); + if (type.keysForBeneficiariesSql().contains(":psm")) q.setParameter("psm", scope.psm); + List rows = q.getResultList(); + List keys = new ArrayList<>(rows.size()); + for (Object r : rows) keys.add(((Number) r).longValue()); + return keys; + } + + // ── Records, in the same JSON each type's existing endpoint returns ─────── + + private Map build(StopTBSyncType type, List keys, String authorisation) { + Map out = new HashMap<>(); + if (keys.isEmpty()) return out; + try { + switch (type) { + case BENEFICIARY: + return beneficiaryService.getBeneficiaryRecordsByRegIds(keys, authorisation); + case TB_SCREENING: + stopTBService.getNurseTBScreeningsByIds(keys).forEach((k, m) -> out.put(k, stopTbGson.toJson(m))); + return out; + case GENERAL_EXAMINATION: + stopTBService.getGeneralExaminationsByIds(keys).forEach((k, m) -> out.put(k, stopTbGson.toJson(m))); + return out; + case GENERAL_OPD: + stopTBService.getGeneralOpdByVisitIds(keys).forEach((k, m) -> out.put(k, stopTbGson.toJson(m))); + return out; + case GENERAL_OPD_LEGACY: + stopTBService.getLegacyGeneralOpdByIds(keys).forEach((k, m) -> out.put(k, stopTbGson.toJson(m))); + return out; + case DIAGNOSTICS: + stopTBService.getDiagnosticsByIds(keys).forEach((k, m) -> out.put(k, stopTbGson.toJson(m))); + return out; + case TB_SUSPECTED: + return tbSuspectedService.getRecordsByIds(keys); + case TB_CONFIRMED: + return tbConfirmedCaseService.getRecordsByIds(keys); + case FORM_RESPONSE: + for (Map.Entry e : dynamicFormResponseService.getResponsesByIds(keys).entrySet()) { + out.put(e.getKey(), objectMapper.writeValueAsString(e.getValue())); + } + return out; + case DIAGNOSTIC_ORDER: + for (Map.Entry> e : diagnosticOrderService.getOrderRecordsByIds(keys).entrySet()) { + out.put(e.getKey(), objectMapper.writeValueAsString(e.getValue())); + } + return out; + default: + return out; + } + } catch (Exception e) { + // A failed batch must not turn into deletes on the tablet: fail the whole call, so the + // token doesn't move and the tablet retries. + throw new IllegalStateException("Could not build " + type.apiName() + " records: " + e.getMessage(), e); + } + } + + private Map item(StopTBSyncType type, String op, long key, long version, String data) { + Map m = new LinkedHashMap<>(); + m.put("type", type.apiName()); + m.put("op", op); + m.put("key", key); + m.put("version", version); + // The record exactly as the type's existing endpoint returns it, so the app reuses its parser. + m.put("data", data == null ? null : JsonParser.parseString(data)); + return m; + } + + private Map page(String serverId, boolean snapshot, List> items, + SyncToken next, boolean hasMore) { + Map out = new LinkedHashMap<>(); + out.put("supported", true); + out.put("serverId", serverId); + out.put("snapshot", snapshot); + out.put("items", items); + out.put("nextToken", next.encode()); + out.put("hasMore", hasMore); + return out; + } + + // The user's services and villages from their role mappings, cached briefly because tablets poll. + private UserSyncScope userScope(Integer userId) { + long now = System.currentTimeMillis(); + Map.Entry cached = userScopes.get(userId); + if (cached != null && now - cached.getKey() < USER_SCOPE_TTL_MS) return cached.getValue(); + UserSyncScope scope = UserSyncScope.fromMappingRows(userServiceRoleRepo.getServiceVillageRows(userId)); + userScopes.put(userId, Map.entry(now, scope)); + return scope; + } + + // ── Hub state ───────────────────────────────────────────────────────────── + + private String serverId() { + return String.valueOf(em.createNativeQuery("SELECT @@server_uuid").getSingleResult()); + } + + /** + * The highest seq a reader may move past. seq is taken when a row is inserted, not when its + * transaction commits, so a transaction still open may hold a lower seq than lines already + * committed. Every line it writes is stamped at or after its start, so lines older than the oldest + * open transaction (information_schema.innodb_trx) are final; the lag also covers the gap between a + * statement's timestamp and its insert. A long open transaction holds the feed back until it ends. + */ + private long safePoint() { + try { + Object r = em.createNativeQuery("SELECT COALESCE(MAX(seq), 0) FROM db_iemr.t_sync_change_log " + + "WHERE changed_at <= (SELECT LEAST(NOW(6), COALESCE(MIN(trx_started), NOW(6))) " + + "FROM information_schema.innodb_trx) - INTERVAL " + SAFE_LAG_SECONDS + " SECOND") + .getSingleResult(); + return ((Number) r).longValue(); + } catch (Exception e) { + // innodb_trx needs the PROCESS privilege; without it only the fixed lag protects late commits. + if (!innodbTrxWarned) { + logger.warn("STOP-TB changes feed: cannot read information_schema.innodb_trx ({}); " + + "using a fixed {} s lag only", e.getMessage(), SAFE_LAG_SECONDS); + innodbTrxWarned = true; + } + Object r = em.createNativeQuery("SELECT COALESCE(MAX(seq), 0) FROM db_iemr.t_sync_change_log " + + "WHERE changed_at <= NOW(6) - INTERVAL " + SAFE_LAG_SECONDS + " SECOND") + .getSingleResult(); + return ((Number) r).longValue(); + } + } + + /** + * True only where the change log exists AND every source table has its insert, update and delete + * triggers. Otherwise a change could be missed silently, so the feed reports itself unsupported and + * the app keeps using the full pull. + */ + @SuppressWarnings("unchecked") + boolean isSupported() { + long now = System.currentTimeMillis(); + if (supported != null && now - supportCheckedAt < SUPPORT_CHECK_TTL_MS) return supported; + boolean ok; + try { + Number logTables = (Number) em.createNativeQuery("SELECT COUNT(*) FROM information_schema.tables " + + "WHERE table_schema = 'db_iemr' AND table_name = 't_sync_change_log'").getSingleResult(); + Set withAllTriggers = new HashSet<>(); + List rows = em.createNativeQuery("SELECT LOWER(CONCAT(event_object_schema, '.', event_object_table)), " + + "COUNT(DISTINCT event_manipulation) FROM information_schema.triggers " + + "WHERE trigger_name LIKE 'trg\\_scl\\_%' GROUP BY event_object_schema, event_object_table").getResultList(); + for (Object[] r : rows) { + String table = (String) r[0]; + // The flow table only decides scope: insert and update are enough (rows are never deleted). + int needed = FLOW_TABLE.equals(table) ? 2 : 3; + if (((Number) r[1]).intValue() >= needed) withAllTriggers.add(table); + } + List missing = new ArrayList<>(); + for (StopTBSyncType type : StopTBSyncType.values()) { + for (String table : type.sourceTables()) { + if (!withAllTriggers.contains(table)) missing.add(table); + } + } + ok = logTables.intValue() > 0 && missing.isEmpty(); + if (logTables.intValue() > 0 && !missing.isEmpty()) { + logger.error("STOP-TB changes feed disabled: change-log triggers missing on {}", missing); + } + } catch (Exception e) { + logger.warn("STOP-TB changes feed: could not check the change log: {}", e.getMessage()); + ok = false; + } + supported = ok; + supportCheckedAt = now; + return ok; + } + + private static Set requestedTypes(List names) { + Set out = EnumSet.noneOf(StopTBSyncType.class); + if (names == null || names.isEmpty()) { + out.addAll(EnumSet.allOf(StopTBSyncType.class)); + return out; + } + for (StopTBSyncType t : StopTBSyncType.values()) { + if (names.contains(t.apiName())) out.add(t); + } + if (out.isEmpty()) throw new IllegalArgumentException("No known record types in " + names); + return out; + } + + private static final class Scope { + final Integer psm; + final List villages; + final Set types; + + Scope(Integer psm, List villages, Set types) { + this.psm = psm; + this.villages = villages; + this.types = types; + } + + long scopeHash() { + return UserSyncScope.scopeHash(psm, villages); + } + + long typesMask() { + long mask = 0; + for (StopTBSyncType t : types) mask |= 1L << t.ordinal(); + return mask; + } + + List logEntities() { + List out = new ArrayList<>(); + for (StopTBSyncType t : types) out.add(t.logEntity()); + return out; + } + } +} diff --git a/src/main/java/com/iemr/flw/service/impl/SupervisorDashboardServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/SupervisorDashboardServiceImpl.java index 771810e20..11ab7f88e 100644 --- a/src/main/java/com/iemr/flw/service/impl/SupervisorDashboardServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/SupervisorDashboardServiceImpl.java @@ -1,20 +1,22 @@ package com.iemr.flw.service.impl; +import java.math.BigDecimal; import java.sql.Timestamp; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.*; import java.util.stream.Collectors; +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.IncentiveActivity; import com.iemr.flw.domain.iemr.IncentiveActivityRecord; -import com.iemr.flw.dto.iemr.UserServiceRoleDTO; +import com.iemr.flw.dto.iemr.*; +import com.iemr.flw.dto.iemr.Period; import com.iemr.flw.masterEnum.IncentiveApprovalStatus; +import com.iemr.flw.masterEnum.StateCode; import com.iemr.flw.repo.iemr.*; +import com.iemr.flw.service.NotificationService; import com.iemr.flw.service.UserService; import com.iemr.flw.utils.JwtUtil; import org.json.JSONArray; @@ -44,15 +46,32 @@ public class SupervisorDashboardServiceImpl implements SupervisorDashboardServic @Autowired private IncentiveRecordRepo incentiveRecordRepo; + @Autowired + private IncentivesRepo incentivesRepo; + @Autowired private JwtUtil jwtUtil; @Autowired private UserService userService; + @Autowired + EmployeeMasterRepo employeeMasterRepo; + + @Autowired + private NotificationService notificationService; + + @Autowired + private UtpreronaPaymentIntegrationImpl paymentService; + @Override - public String getSupervisorDashboard(Integer supervisorUserID, Integer month, Integer year) { + public String getSupervisorDashboard(Integer supervisorUserID, Integer month, Integer year,Integer facilityId) { JSONObject result = new JSONObject(); + Integer stateId = userService.getUserDetail(supervisorUserID).getStateId(); + String rollName = userService.getUserDetail(supervisorUserID).getRoleName(); + + LocalDate today = LocalDate.now(); + // 1. Supervisor user details List supervisorRows = dashboardRepo.getSupervisorUserDetails(supervisorUserID); @@ -66,28 +85,99 @@ public String getSupervisorDashboard(Integer supervisorUserID, Integer month, In supervisor.put("gender", str(sRow[5]).isEmpty() ? JSONObject.NULL : str(sRow[5])); result.put("supervisor", supervisor); } + logger.info("Supervisor id"+supervisorUserID); // 2. Get all ASHAs with facility info - List ashaRows = dashboardRepo.getAshasWithFacilityInfo(supervisorUserID); + logger.info("Fetching ASHA details for supervisorUserID: {}", supervisorUserID); + + + LocalDate dueDate = LocalDate.of(year, month, 1) + .plusMonths(1) + .withDayOfMonth(5); + + boolean isOverDue = + (rollName.equalsIgnoreCase("ANM") || rollName.equalsIgnoreCase("ASHA Supervisor")) + && today.isAfter(dueDate); + + + List ashaRows; + + if ("ANM".equalsIgnoreCase(rollName) || "CHO".equalsIgnoreCase(rollName)) { + if(facilityId.equals(0)){ + List facilityIDs = + facilityLoginRepo.getUserFacilityIDs(supervisorUserID); + + ashaRows = + facilityLoginRepo.getAshaListByFacilities(facilityIDs); + }else { + + + ashaRows = facilityLoginRepo.getAshaListByFacilities( + Collections.singletonList(facilityId)); + } + + + } else { + if(facilityId.equals(0)){ + List facilityIDs = + facilityLoginRepo.getUserFacilityIDs(supervisorUserID); + + ashaRows = + facilityLoginRepo.getAshaListByFacilities(facilityIDs); + }else { + ashaRows = facilityLoginRepo.getAshaListByFacilities( + Collections.singletonList(facilityId)); + } + + + } + if (ashaRows == null || ashaRows.isEmpty()) { + logger.warn("No ASHA records found for supervisorUserID: {}", supervisorUserID); + result.put("totalAshaCount", 0); result.put("incentiveSummary", buildEmptyIncentiveSummary()); result.put("facilities", new JSONArray()); + + logger.info("Returning empty dashboard response for supervisorUserID: {}", supervisorUserID); + return result.toString(); } + + // Collect distinct facility IDs and ASHA IDs + + logger.info("Processing {} ASHA records", ashaRows.size()); + Set facilityIDSet = new HashSet<>(); Set ashaIDSet = new HashSet<>(); + for (Object[] row : ashaRows) { - if (row[3] != null) - facilityIDSet.add((Integer) row[3]); + + logger.info("Complete Row: {}", Arrays.toString(row)); + + for (int i = 0; i < row.length; i++) { + logger.info("row[{}] = {} ({})", + i, + row[i], + row[i] != null ? row[i].getClass().getName() : "null"); + } + + + if (row[4] != null) + facilityIDSet.add((Integer) row[4]); + if (row[0] != null) ashaIDSet.add((Integer) row[0]); + } + List facilityIDs = new ArrayList<>(facilityIDSet); List ashaIDs = new ArrayList<>(ashaIDSet); + logger.info("Facility IDs: {}", facilityIDs); + logger.info("ASHA IDs: {}", ashaIDs); result.put("totalAshaCount", ashaIDs.size()); // 3. Location from first facility @@ -116,7 +206,7 @@ public String getSupervisorDashboard(Integer supervisorUserID, Integer month, In } // 5. Get incentive status per ASHA (verified, rejected, pending, totalAmount) - long overallVerified = 0, overallRejected = 0, overallPending = 0; + long overallVerified = 0, overallRejected = 0, overallPending = 0, overallOverDue =0; long overallUnclaimed = 0; try { @@ -133,27 +223,181 @@ public String getSupervisorDashboard(Integer supervisorUserID, Integer month, In Timestamp endDate = Timestamp.valueOf(endLocalDate.atStartOfDay()); logger.info("Asha ID" + ashaIDs); - List statusRows = dashboardRepo.getIncentiveStatusByAshaIds(ashaIDs, startDate, endDate); - if (statusRows != null) { - for (Object[] sRow : statusRows) { - long verified = ((Number) sRow[2]).longValue(); - long rejected = ((Number) sRow[3]).longValue(); - long pending = ((Number) sRow[4]).longValue(); + if(stateId.equals(StateCode.AM.getStateCode())){ + List statusRows = dashboardRepo.getIncentiveStatusByAshaIds(ashaIDs, startDate, endDate); + if (statusRows != null) { + for (Object[] sRow : statusRows) { + long verified = ((Number) sRow[2]).longValue(); + long rejected = ((Number) sRow[3]).longValue(); + long pending = ((Number) sRow[4]).longValue(); + + if (verified > 0) overallVerified += 1; + if (rejected > 0) overallRejected += 1; + if (pending > 0) overallPending += 1; + } + } + List unclaimedRows = dashboardRepo.getUnclaimedCountByAshaIds(ashaIDs, startDate, endDate); + if (unclaimedRows != null) { + for (Object[] uRow : unclaimedRows) { + long count = ((Number) uRow[1]).longValue(); + if (count > 0) overallUnclaimed += 1; + } + } + }else if (stateId.equals(StateCode.CG.getStateCode())) { + + if ("ASHA Supervisor".equalsIgnoreCase(rollName)) { + + List statusRows = + dashboardRepo.getDefaultIncentiveStatusByAshaIds( + ashaIDs, + startDate, + endDate + ); + + if (statusRows != null) { + for (Object[] statusRow : statusRows) { + + long verified = statusRow[5] != null + ? ((Number) statusRow[5]).longValue() + : 0L; + + long rejected = statusRow[3] != null + ? ((Number) statusRow[3]).longValue() + : 0L; + + long pending = statusRow[4] != null + ? ((Number) statusRow[4]).longValue() + : 0L; + + if (verified > 0) { + overallVerified++; + } + + if (rejected > 0) { + overallRejected++; + } + + if (pending > 0) { + if (isOverDue) { + overallOverDue++; + } else { + overallPending++; + } + } + } + } - if (verified > 0) overallVerified += 1; - if (rejected > 0) overallRejected += 1; - if (pending > 0) overallPending += 1; + // ASHA Supervisor: only default unclaimed activities + List unclaimedRows = + dashboardRepo.getDefaultUnclaimedCountByAshaIds( + ashaIDs, + startDate, + endDate + ); + + if (unclaimedRows != null) { + for (Object[] unclaimedRow : unclaimedRows) { + + Integer ashaId = unclaimedRow[0] != null + ? ((Number) unclaimedRow[0]).intValue() + : null; + + long unclaimedCount = unclaimedRow[1] != null + ? ((Number) unclaimedRow[1]).longValue() + : 0L; + + logger.info( + "Supervisor unclaimed count: ashaId={}, count={}", + ashaId, + unclaimedCount + ); + + if (unclaimedCount > 0) { + overallUnclaimed++; + } + } } - } - List unclaimedRows = dashboardRepo.getUnclaimedCountByAshaIds(ashaIDs, startDate, endDate); - if (unclaimedRows != null) { - for (Object[] uRow : unclaimedRows) { - long count = ((Number) uRow[1]).longValue(); - if (count > 0) overallUnclaimed += 1; + + } else if ("ANM".equalsIgnoreCase(rollName) + || "CHO".equalsIgnoreCase(rollName)) { + + List statusRows = + dashboardRepo.getIncentiveStatusByAshaIdsForAnm( + ashaIDs, + startDate, + endDate + ); + + if (statusRows != null) { + for (Object[] statusRow : statusRows) { + + long verified = statusRow[2] != null + ? ((Number) statusRow[2]).longValue() + : 0L; + + long rejected = statusRow[3] != null + ? ((Number) statusRow[3]).longValue() + : 0L; + + long pending = statusRow[5] != null + ? ((Number) statusRow[5]).longValue() + : 0L; + + if (verified > 0) { + overallVerified++; + } + + if (rejected > 0) { + overallRejected++; + } + + if (pending > 0) { + if (isOverDue) { + overallOverDue++; + } else { + overallPending++; + } + } + } + } + + // ANM/CHO: all unclaimed activities + List unclaimedRows = + dashboardRepo.getUnclaimedCountByAshaIds( + ashaIDs, + startDate, + endDate + ); + + if (unclaimedRows != null) { + for (Object[] unclaimedRow : unclaimedRows) { + + Integer ashaId = unclaimedRow[0] != null + ? ((Number) unclaimedRow[0]).intValue() + : null; + + long unclaimedCount = unclaimedRow[1] != null + ? ((Number) unclaimedRow[1]).longValue() + : 0L; + + logger.info( + "ANM/CHO unclaimed count: ashaId={}, count={}", + ashaId, + unclaimedCount + ); + + if (unclaimedCount > 0) { + overallUnclaimed++; + } + } } } + } + + } catch (Exception e) { logger.error("Error fetching incentive status: " + e.getMessage(), e); + } @@ -163,14 +407,14 @@ public String getSupervisorDashboard(Integer supervisorUserID, Integer month, In overallSummary.put("verified", overallVerified); overallSummary.put("rejected", overallRejected); overallSummary.put("pending", overallPending); - overallSummary.put("overDue", 0); + overallSummary.put("overDue", overallOverDue); overallSummary.put("unclaimed", overallUnclaimed); result.put("incentiveSummary", overallSummary); // 7. Build facilities array with nested ASHAs Map> ashasByFacility = new HashMap<>(); for (Object[] row : ashaRows) { - Integer facID = (Integer) row[3]; + Integer facID = (Integer) row[4]; ashasByFacility.computeIfAbsent(facID, k -> new ArrayList<>()).add(row); } @@ -204,7 +448,6 @@ public String getSupervisorDashboard(Integer supervisorUserID, Integer month, In asha.put("fullName", fullName(row[1], row[2])); asha.put("employeeId", str(row[6]).isEmpty() ? JSONObject.NULL : str(row[6])); asha.put("mobile", str(row[7]).isEmpty() ? JSONObject.NULL : str(row[7])); - asha.put("gender", str(row[8]).isEmpty() ? JSONObject.NULL : str(row[8])); ashasArray.put(asha); } @@ -218,10 +461,360 @@ public String getSupervisorDashboard(Integer supervisorUserID, Integer month, In return result.toString(); } + + @Override - public Map getAshasAtFacility(Integer supervisorId, Integer facilityId, - Integer month, Integer year, Integer approvalStatusID) { - List rows; + public String getSupervisorSubCenter(Integer supervisorUserID, Integer month, Integer year) { + JSONObject result = new JSONObject(); + Integer stateId = userService.getUserDetail(supervisorUserID).getStateId(); + String rollName = userService.getUserDetail(supervisorUserID).getRoleName(); + + + // 1. Supervisor user details + List supervisorRows = dashboardRepo.getSupervisorUserDetails(supervisorUserID); + if (supervisorRows != null && !supervisorRows.isEmpty()) { + Object[] sRow = supervisorRows.get(0); + JSONObject supervisor = new JSONObject(); + supervisor.put("userId", sRow[0]); + supervisor.put("fullName", fullName(sRow[1], sRow[2])); + supervisor.put("employeeId", str(sRow[3]).isEmpty() ? JSONObject.NULL : str(sRow[3])); + supervisor.put("mobile", str(sRow[4]).isEmpty() ? JSONObject.NULL : str(sRow[4])); + supervisor.put("gender", str(sRow[5]).isEmpty() ? JSONObject.NULL : str(sRow[5])); + result.put("supervisor", supervisor); + } + logger.info("Supervisor id"+supervisorUserID); + + // 2. Get all ASHAs with facility info + logger.info("Fetching ASHA details for supervisorUserID: {}", supervisorUserID); + + + LocalDate today = LocalDate.now(ZoneId.of("Asia/Kolkata")); + + LocalDate dueDate = LocalDate.of(year, month, 1) + .plusMonths(1) + .withDayOfMonth(5); + + boolean isOverDue = + "ASHA Supervisor".equalsIgnoreCase(rollName) + && today.isAfter(dueDate); + + + List ashaRows; + + if ("ANM".equalsIgnoreCase(rollName) || "CHO".equalsIgnoreCase(rollName)) { + List facilityIDs = + facilityLoginRepo.getUserFacilityIDs(supervisorUserID); + + ashaRows = + facilityLoginRepo.getAshaListByFacilities(facilityIDs); + + + } else { + ashaRows = + dashboardRepo.getAshasWithFacilityInfo(supervisorUserID); + + + } + + if (ashaRows == null || ashaRows.isEmpty()) { + logger.warn("No ASHA records found for supervisorUserID: {}", supervisorUserID); + + result.put("totalAshaCount", 0); + result.put("incentiveSummary", buildEmptyIncentiveSummary()); + result.put("facilities", new JSONArray()); + + logger.info("Returning empty dashboard response for supervisorUserID: {}", supervisorUserID); + + return result.toString(); + } + + + + // Collect distinct facility IDs and ASHA IDs + + logger.info("Processing {} ASHA records", ashaRows.size()); + + Set facilityIDSet = new HashSet<>(); + Set ashaIDSet = new HashSet<>(); + + for (Object[] row : ashaRows) { + + logger.info("Complete Row: {}", Arrays.toString(row)); + + for (int i = 0; i < row.length; i++) { + logger.info("row[{}] = {} ({})", + i, + row[i], + row[i] != null ? row[i].getClass().getName() : "null"); + } + + + if (row[4] != null) + facilityIDSet.add((Integer) row[4]); + + if (row[0] != null) + ashaIDSet.add((Integer) row[0]); + + } + + List facilityIDs = new ArrayList<>(facilityIDSet); + List ashaIDs = new ArrayList<>(ashaIDSet); + + logger.info("Facility IDs: {}", facilityIDs); + logger.info("ASHA IDs: {}", ashaIDs); + result.put("totalAshaCount", ashaIDs.size()); + + // 3. Location from first facility + List facilityRows = dashboardRepo.getFacilityDetails(facilityIDs); + if (facilityRows != null && !facilityRows.isEmpty()) { + Object[] fRow = facilityRows.get(0); + JSONObject location = new JSONObject(); + location.put("state", str(fRow[2])); + location.put("district", str(fRow[3])); + location.put("blockOrUlb", str(fRow[4])); + location.put("locationType", str(fRow[5])); + result.put("location", location); + } + + // 4. Build village map (facilityID -> villages) + Map> villageMap = new HashMap<>(); + List villageRows = dashboardRepo.getVillagesForFacilities(facilityIDs); + if (villageRows != null) { + for (Object[] vRow : villageRows) { + Integer facID = (Integer) vRow[0]; + JSONObject village = new JSONObject(); + village.put("villageId", vRow[1]); + village.put("villageName", str(vRow[2])); + villageMap.computeIfAbsent(facID, k -> new ArrayList<>()).add(village); + } + } + + // 5. Get incentive status per ASHA (verified, rejected, pending, totalAmount) + long overallVerified = 0, overallRejected = 0, overallPending = 0, overallOverDue =0; + long overallUnclaimed = 0; + + try { + logger.info("Month: {}", month); + logger.info("Year: {}", year); + + LocalDate startLocalDate = LocalDate.of(year, month, 1); + LocalDate endLocalDate = startLocalDate.plusMonths(1); + + logger.info("startLocalDate {}", startLocalDate); + logger.info("endLocalDate {}", endLocalDate); + + Timestamp startDate = Timestamp.valueOf(startLocalDate.atStartOfDay()); + Timestamp endDate = Timestamp.valueOf(endLocalDate.atStartOfDay()); + logger.info("Asha ID" + ashaIDs); + + if(stateId.equals(StateCode.AM.getStateCode())){ + List statusRows = dashboardRepo.getIncentiveStatusByAshaIds(ashaIDs, startDate, endDate); + if (statusRows != null) { + for (Object[] sRow : statusRows) { + long verified = ((Number) sRow[2]).longValue(); + long rejected = ((Number) sRow[3]).longValue(); + long pending = ((Number) sRow[4]).longValue(); + + if (verified > 0) overallVerified += 1; + if (rejected > 0) overallRejected += 1; + if (pending > 0) overallPending += 1; + } + } + List unclaimedRows = dashboardRepo.getUnclaimedCountByAshaIds(ashaIDs, startDate, endDate); + if (unclaimedRows != null) { + for (Object[] uRow : unclaimedRows) { + long count = ((Number) uRow[1]).longValue(); + if (count > 0) overallUnclaimed += 1; + } + } + }else if(stateId.equals(StateCode.CG.getStateCode())){ + if("ASHA Supervisor".equalsIgnoreCase(rollName)){ + List statusRows = dashboardRepo.getDefaultIncentiveStatusByAshaIds(ashaIDs, startDate, endDate); + logger.info( + "Supervisor dashboard: role={}, today={}, dueDate={}, " + + "isOverDue={}, month={}, year={}, ashaIDs={}", + rollName, + today, + dueDate, + isOverDue, + month, + year, + ashaIDs + ); + + if (statusRows != null) { + for (Object[] statusRow : statusRows) { + + Integer ashaId = statusRow[0] != null + ? ((Number) statusRow[0]).intValue() + : null; + + long verified = statusRow[2] != null + ? ((Number) statusRow[2]).longValue() + : 0L; + + long rejected = statusRow[3] != null + ? ((Number) statusRow[3]).longValue() + : 0L; + + long pending = statusRow[4] != null + ? ((Number) statusRow[4]).longValue() + : 0L; + + logger.info( + "Supervisor status: ashaId={}, verified={}, rejected={}, " + + "pending={}, isOverDue={}", + ashaId, + verified, + rejected, + pending, + isOverDue + ); + + if (verified > 0) { + overallVerified++; + } + + if (rejected > 0) { + overallRejected++; + } + + if (pending > 0) { + if (isOverDue) { + overallOverDue++; + } else { + overallPending++; + } + } + } + } + }else if("ANM".equalsIgnoreCase(rollName)){ + List statusRows = dashboardRepo.getIncentiveStatusByAshaIdsForAnm(ashaIDs, startDate, endDate); + if (statusRows != null) { + for (Object[] sRow : statusRows) { + long verified = ((Number) sRow[2]).longValue(); + long rejected = ((Number) sRow[3]).longValue(); + long pending = ((Number) sRow[5]).longValue(); + + if (verified > 0) overallVerified += 1; + if (rejected > 0) overallRejected += 1; + if (pending > 0) { + if (isOverDue) { + overallOverDue++; + } else { + overallPending++; + } + } + } + } + } + + List unclaimedRows = dashboardRepo.getUnclaimedCountByAshaIds(ashaIDs, startDate, endDate); + if (unclaimedRows != null) { + for (Object[] uRow : unclaimedRows) { + long count = ((Number) uRow[1]).longValue(); + if (count > 0) overallUnclaimed += 1; + } + } + } + + + } catch (Exception e) { + logger.error("Error fetching incentive status: " + e.getMessage(), e); + + } + + + + // Overall incentive summary across all ASHAs + JSONObject overallSummary = new JSONObject(); + overallSummary.put("verified", overallVerified); + overallSummary.put("rejected", overallRejected); + overallSummary.put("pending", overallPending); + overallSummary.put("overDue", overallOverDue); + overallSummary.put("unclaimed", overallUnclaimed); + result.put("incentiveSummary", overallSummary); + + // 7. Build facilities array with nested ASHAs + Map> ashasByFacility = new HashMap<>(); + for (Object[] row : ashaRows) { + Integer facID = (Integer) row[4]; + ashasByFacility.computeIfAbsent(facID, k -> new ArrayList<>()).add(row); + } + + Map facilityDetailsMap = new HashMap<>(); + if (facilityRows != null) { + for (Object[] fRow : facilityRows) { + facilityDetailsMap.put((Integer) fRow[0], fRow); + } + } + + JSONArray facilitiesArray = new JSONArray(); + for (Integer facID : facilityIDs) { + JSONObject facility = new JSONObject(); + facility.put("facilityId", facID); + + Object[] fDetails = facilityDetailsMap.get(facID); + if (fDetails != null) { + facility.put("facilityName", str(fDetails[1])); + facility.put("facilityType", str(fDetails[6])); + } + + + // ASHAs at this facility + JSONArray ashasArray = new JSONArray(); + List facAshaRows = ashasByFacility.get(facID); + if (facAshaRows != null) { + for (Object[] row : facAshaRows) { + Integer ashaId = (Integer) row[0]; + JSONObject asha = new JSONObject(); + asha.put("userId", ashaId); + asha.put("fullName", fullName(row[1], row[2])); + asha.put("employeeId", str(row[6]).isEmpty() ? JSONObject.NULL : str(row[6])); + asha.put("mobile", str(row[7]).isEmpty() ? JSONObject.NULL : str(row[7])); + + ashasArray.put(asha); + } + } + + facility.put("ashaCount", ashasArray.length()); + facilitiesArray.put(facility); + } + + result.put("facilities", facilitiesArray); + return result.toString(); + } + + + + @Override + public Map getAshasAtFacility(Integer supervisorId, Integer facilityId, + Integer month, Integer year, Integer approvalStatusID) { + Integer supervisorstateCode = userService.getUserDetail(supervisorId).getStateId(); + List rows = null; + LocalDate today = LocalDate.now(); + + + String roleName = userService.getUserDetail(supervisorId).getRoleName(); + + LocalDate currentMonthStartDate = LocalDate.of(year, month, 1); + LocalDate currentMonthendLocalDate = currentMonthStartDate.plusMonths(1); + + LocalDate dueDate = LocalDate.of(year, month, 1) + .plusMonths(1) + .withDayOfMonth(5); + + boolean isOverDue = + ("ANM".equalsIgnoreCase(roleName) + || "ASHA Supervisor".equalsIgnoreCase(roleName)) + && today.isAfter(dueDate); + + + logger.info("Login user role:"+ roleName); + + boolean isCurrentMonth = + today.getYear() == year && + today.getMonthValue() == month; logger.info("approvalStatusID:" + approvalStatusID); List superVisorRow = ashaSupervisorLoginRepo.getAllMappedAshas(supervisorId); @@ -234,15 +827,56 @@ public Map getAshasAtFacility(Integer supervisorId, Integer faci Timestamp startDate = Timestamp.valueOf(startLocalDate.atStartOfDay()); Timestamp endDate = Timestamp.valueOf(endLocalDate.atStartOfDay()); - if (facilityId.equals(0)) { - rows = ashaSupervisorLoginRepo.getAshasAtFacility( - supervisorId, approvalStatusID, startDate, endDate); + + + + if ("ANM".equalsIgnoreCase(roleName) || "CHO".equalsIgnoreCase(roleName)) { + logger.info("ANM:" + roleName); + + if (facilityId.equals(0)) { + + List facilityIDs = facilityLoginRepo.getUserFacilityIDs(supervisorId); + rows = facilityLoginRepo.getAshaListByFacilities(facilityIDs); + + } else { + + rows = facilityLoginRepo.getAshaListByFacilities( + Collections.singletonList(facilityId)); + + } + } else { - rows = ashaSupervisorLoginRepo.getAshasAtFacility( - supervisorId, facilityId, approvalStatusID, startDate, endDate); + logger.info("Other:" + roleName); + + if (facilityId.equals(0)) { + if(supervisorstateCode.equals(StateCode.CG.getStateCode())){ + if("ASHA Supervisor".equalsIgnoreCase(roleName)){ + rows = ashaSupervisorLoginRepo.getAshasAtFacilityCg( + supervisorId, approvalStatusID, startDate, endDate); + } + }else if(supervisorstateCode.equals(StateCode.AM.getStateCode())){ + rows = ashaSupervisorLoginRepo.getAshasAtFacility( + supervisorId, approvalStatusID, startDate, endDate); + } + } else { + if(supervisorstateCode.equals(StateCode.CG.getStateCode())){ + if("ASHA Supervisor".equalsIgnoreCase(roleName)){ + rows = ashaSupervisorLoginRepo.getAshasAtFacilityCg( + supervisorId, facilityId, approvalStatusID, startDate, endDate); + } + }else if(supervisorstateCode.equals(StateCode.AM.getStateCode())){ + rows = ashaSupervisorLoginRepo.getAshasAtFacility( + supervisorId, facilityId, approvalStatusID, startDate, endDate); + } + + + } + } + logger.info("Other:" + rows); + - long overallVerified = 0, overallRejected = 0, overallPending = 0; + long overallVerified = 0, overallRejected = 0, overallPending = 0 , overallUnclaimed=0 ,overallOverDue =0; String facilityName = ""; String facilityType = ""; @@ -256,26 +890,493 @@ public Map getAshasAtFacility(Integer supervisorId, Integer faci for (Object[] row : rows) { + long pending = 0; + long verified = 0; + long rejected = 0; + long unclaimedCount = 0; + long overDue = 0; + + Map asha = new HashMap<>(); Integer ashaId = ((Number) row[0]).intValue(); + logger.info("ASHA_ID"+ashaId); + List countList =null; + + if(supervisorstateCode.equals(StateCode.CG.getStateCode())){ + + if("ASHA Supervisor".equalsIgnoreCase(roleName)){ + if(approvalStatusID.equals(106)){ + countList = incentiveRecordRepo.getStatusUnclaimedCountByAshaId(ashaId, startDate, endDate); + + }else { + countList = incentiveRecordRepo.getStatusCountByAshaIdOfDefaultActivity(ashaId, startDate, endDate); + + } + + logger.info("countList = {}", Arrays.deepToString(countList.toArray())); + + + + }else if(("ANM".equalsIgnoreCase(roleName) || "CHO".equalsIgnoreCase(roleName) )){ + logger.info("ASHA_ID = {}", ashaId); + logger.info("Role = {}", roleName); + + if(approvalStatusID.equals(106)){ + countList = incentiveRecordRepo.getStatusUnclaimedCountByAshaId(ashaId, startDate, endDate); + + }else { + countList = incentiveRecordRepo.getStatusCountByAshaIdANM(ashaId, startDate, endDate); + + } + + + logger.info("Count List = {}", countList); + + } + + }else if(supervisorstateCode.equals(StateCode.AM.getStateCode())){ + countList = incentiveRecordRepo.getStatusCountByAshaId(ashaId, startDate, endDate); + + } - List countList = incentiveRecordRepo.getStatusCountByAshaId(ashaId, startDate, endDate); Long totalAmount = null; if (userService.getUserDetail(ashaId) != null) { Integer stateCode = userService.getUserDetail(ashaId).getStateId(); - totalAmount = incentiveRecordRepo.getTotalAmountByAsha( - ashaId, startDate, endDate, approvalStatusID, stateCode); + if(stateCode.equals(StateCode.AM.getStateCode())){ + totalAmount = incentiveRecordRepo.getTotalAmountByAsha( + ashaId, startDate, endDate, approvalStatusID, stateCode); + }else if(stateCode.equals(StateCode.CG.getStateCode())){ - } + if("ASHA Supervisor".equalsIgnoreCase(roleName)){ + totalAmount = incentiveRecordRepo.getDefaultActivityTotalAmountByAsha( + ashaId, startDate, endDate, approvalStatusID, stateCode); + }else if("ANM".equalsIgnoreCase(roleName) || "CHO".equalsIgnoreCase(roleName) ){ + totalAmount = incentiveRecordRepo.getTotalAmountByAsha( + ashaId, startDate, endDate, approvalStatusID, stateCode); + } + + } + + + } + Integer stateCode = userService.getUserDetail(ashaId).getStateId(); + + + List incentiveActivityRecord = null; + if(stateCode.equals(StateCode.AM.getStateCode())){ + List dbRecords = + incentiveRecordRepo.getRecordsByAsha(ashaId, startDate, endDate); + incentiveActivityRecord= dbRecords + .stream() + .filter(r -> approvalStatusID == 0 || + approvalStatusID.equals(r.getApprovalStatus())) + .collect(Collectors.toList()); + }else if(stateCode.equals(StateCode.CG.getStateCode())){ + List dbRecords = + incentiveRecordRepo.getRecordsByAsha(ashaId, startDate, endDate); + + if (Objects.equals(approvalStatusID, 104) && isOverDue) { + if("ASHA Supervisor".equalsIgnoreCase(roleName)){ + incentiveActivityRecord = dbRecords.stream() + + // Overdue applicable statuses + + .filter(record -> + (Objects.equals(record.getApprovalStatus(), 102) + || Objects.equals(record.getApprovalStatus(), 104)) && record.getIsDefaultActivity() + ) + + + .peek(record -> { + logger.info( + "Overdue record matched: role={}, ashaId={}, " + + "recordId={}, oldStatus={}, default={}, " + + "approved={}, claimedDate={}", + roleName, + ashaId, + record.getId(), + record.getApprovalStatus(), + record.getIsDefaultActivity(), + record.getIsApproved(), + record.getCalimedDate() + ); + + record.setApprovalStatus(104); + }) + .collect(Collectors.toList()); + + overDue = incentiveActivityRecord.size(); + + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + }else if("ANM".equalsIgnoreCase(roleName) || "CHO".equalsIgnoreCase(roleName)){ + incentiveActivityRecord = dbRecords.stream() + .filter(record -> { + + boolean isDefault = + Boolean.TRUE.equals(record.getIsDefaultActivity()); + + boolean isApproved = + Boolean.TRUE.equals(record.getIsApproved()); + + Integer status = record.getApprovalStatus(); + + // 102: + // Non-default OR approved default activity + boolean status102 = + Objects.equals(status, 102) + && (!isDefault || isApproved); + + // 105: + // Only default activity + boolean status105 = + Objects.equals(status, 105) + && isDefault; + + // Already overdue + boolean status104 = + Objects.equals(status, 104); + + return status102 || status105 || status104; + }) + .peek(record -> { + logger.info( + "Overdue record matched: role={}, ashaId={}, " + + "recordId={}, oldStatus={}, default={}, " + + "approved={}, claimedDate={}", + roleName, + ashaId, + record.getId(), + record.getApprovalStatus(), + record.getIsDefaultActivity(), + record.getIsApproved(), + record.getCalimedDate() + ); + + record.setApprovalStatus(104); + }) + .collect(Collectors.toList()); + + overDue = incentiveActivityRecord.size(); + + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + + } + + + logger.info( + "Overdue final: role={}, ashaId={}, count={}, totalAmount={}", + roleName, + ashaId, + overDue, + totalAmount + ); + } else if("ASHA Supervisor".equalsIgnoreCase(roleName)){ + if (approvalStatusID.equals(102)) { + + incentiveActivityRecord = dbRecords.stream() + .filter(record -> + Objects.equals(record.getApprovalStatus(), 102) + ) + .filter(record -> + Boolean.TRUE.equals( + record.getIsDefaultActivity() + ) + ) + .peek(record -> { + logger.info( + "Supervisor record matched: ashaId={}, " + + "recordId={}, oldStatus={}, isOverDue={}, " + + "defaultActivity={}", + ashaId, + record.getId(), + record.getApprovalStatus(), + isOverDue, + record.getIsDefaultActivity() + ); + + if (isOverDue) { + record.setApprovalStatus(104); + } + }) + .collect(Collectors.toList()); + + if (isOverDue) { + overDue = incentiveActivityRecord.size(); + } + + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + + }else if (approvalStatusID.equals(103)) { + + if (Objects.equals(approvalStatusID, 103)) { + + // ASHA Supervisor can view only rejected default activities + // that are unclaimed + incentiveActivityRecord = dbRecords.stream() + .filter(record -> + Objects.equals(record.getApprovalStatus(), 103) + && Boolean.TRUE.equals( + record.getIsDefaultActivity() + ) + ) + .collect(Collectors.toList()); + } + + + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + + } else if (approvalStatusID.equals(104) && isOverDue) { + + /* + */ + incentiveActivityRecord = dbRecords.stream() + .filter(record -> + Objects.equals(record.getApprovalStatus(), 102) + || Objects.equals( + record.getApprovalStatus(), 104 + ) + || Objects.equals( + record.getApprovalStatus(), 105 + ) + ) + .filter(record -> + Boolean.TRUE.equals( + record.getIsDefaultActivity() + ) + ) + .peek(record -> + record.setApprovalStatus(104) + ) + .collect(Collectors.toList()); + + overDue = incentiveActivityRecord.size(); + + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + + } else if (approvalStatusID.equals(105)) { + + incentiveActivityRecord = dbRecords.stream() + .filter(record -> + ( + Objects.equals( + record.getApprovalStatus(), 101 + ) + || Objects.equals( + record.getApprovalStatus(), 105 + ) + ) + && Boolean.TRUE.equals( + record.getIsDefaultActivity() + ) + ) + .collect(Collectors.toList()); + + } else if (approvalStatusID.equals(106)) { + + incentiveActivityRecord = dbRecords.stream() + .filter(record -> + Objects.equals(record.getApprovalStatus(), 102) + && Boolean.TRUE.equals( + record.getIsDefaultActivity() + ) + && !Boolean.TRUE.equals( + record.getIsClaimed() + ) + ) + .collect(Collectors.toList()); + + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + + }else if (approvalStatusID.equals(0)) { + + incentiveActivityRecord = dbRecords.stream() + .filter(record -> + ( + Objects.equals( + record.getApprovalStatus(), 101 + ) + || Objects.equals( + record.getApprovalStatus(), 102 + ) + || Objects.equals( + record.getApprovalStatus(), 103 + ) + || Objects.equals( + record.getApprovalStatus(), 104 + ) + || Objects.equals( + record.getApprovalStatus(), 105 + ) + ) + && Boolean.TRUE.equals( + record.getIsDefaultActivity() + ) + ) + .collect(Collectors.toList()); + + } else { + + incentiveActivityRecord = Collections.emptyList(); + } + + }else if ("ANM".equalsIgnoreCase(roleName) || "CHO".equalsIgnoreCase(roleName)) { + if (approvalStatusID.equals(102)) { + if (Objects.equals(approvalStatusID, 102)) { + + incentiveActivityRecord = dbRecords.stream() + .peek(record -> { + if (Objects.equals(record.getApprovalStatus(), 102) + && Boolean.TRUE.equals(record.getIsDefaultActivity()) + && Boolean.TRUE.equals(record.getIsApproved())) { + + logger.info("Changing record status 102 to 105: id={}, activityId={}", + record.getId(), record.getActivityId()); + + record.setApprovalStatus(105); + } + }) + .filter(record -> { + boolean isDefault = Boolean.TRUE.equals(record.getIsDefaultActivity()); + boolean isClaimed = Boolean.TRUE.equals(record.getIsClaimed()); + Integer status = record.getApprovalStatus(); + boolean default105 = isDefault && Objects.equals(status, 105); + + + boolean nonDefault102Claimed = !isDefault && Objects.equals(status, 102) && isClaimed; + + boolean other105 = !isDefault && Objects.equals(status, 105); + + return nonDefault102Claimed || default105 || other105; + + }) + .collect(Collectors.toList()); + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + + logger.info( + "ANM final filtered records: count={}, totalAmount={}", + incentiveActivityRecord.size(), + totalAmount + ); + + incentiveActivityRecord.forEach(record -> + logger.info( + "Final record: id={}, activityId={}, status={}, " + + "default={}, approved={}", + record.getId(), + record.getActivityId(), + record.getApprovalStatus(), + record.getIsDefaultActivity(), + record.getIsApproved() + ) + ); + } + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + + dbRecords.forEach(r -> + logger.info( + "DB Record -> id: {}, approvalStatus: {}, ashaId: {}, benId: {}, activityId: {}", + r.getId(), + r.getApprovalStatus(), + r.getAshaId(), + r.getBenId(), + r.getActivityId() + ) + ); + }else if (Objects.equals(approvalStatusID, 103)) { + + // ANM/CHO can view all rejected activities + // that are unclaimed + incentiveActivityRecord = dbRecords.stream() + .filter(record -> + Objects.equals(record.getApprovalStatus(), 103) + + ) + .collect(Collectors.toList()); + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + }else if(approvalStatusID.equals(106)){ + incentiveActivityRecord = dbRecords.stream() + .filter(r->!r.getIsClaimed() && r.getApprovalStatus().equals(102)).peek(r->{r.setApprovalStatus(106);}).collect(Collectors.toList()); + totalAmount = incentiveActivityRecord.stream() + .map(IncentiveActivityRecord::getAmount) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + }else if(approvalStatusID.equals(0)){ + incentiveActivityRecord = dbRecords.stream() + .filter(r->((r.getApprovalStatus().equals(102) && r.getIsClaimed()) ||(r.getApprovalStatus().equals(102) && !r.getIsClaimed()) || r.getApprovalStatus().equals(103) || r.getApprovalStatus().equals(105) || r.getApprovalStatus().equals(101) || r.getApprovalStatus().equals(104))).collect(Collectors.toList()); + } else{ + incentiveActivityRecord = dbRecords.stream() + .filter(r -> { + + if (approvalStatusID != 0 + && !approvalStatusID.equals(r.getApprovalStatus())) { + return false; + } + + // 102 should be visible only after 24 hours + if (r.getApprovalStatus().equals(102)) { + return true; + } + + // 105 should always be visible + return true; + }) + .peek(r -> { + if (r.getApprovalStatus().equals(102)) { + r.setApprovalStatus(105); + } + }) + .collect(Collectors.toList()); + } + + } + + + logger.info("CG - Records after approvalStatus filter: {}", incentiveActivityRecord.size()); + + } + logger.info("Final incentiveActivityRecord count: {}", incentiveActivityRecord.size()); + logger.info("Final incentiveActivityRecord: {}", + new Gson().toJson(incentiveActivityRecord)); - List incentiveActivityRecord = - incentiveRecordRepo.getRecordsByAsha(ashaId, startDate, endDate) - .stream() - .filter(r -> approvalStatusID == 0 || - approvalStatusID.equals(r.getApprovalStatus())) - .collect(Collectors.toList()); List> activityList = new ArrayList<>(); for (IncentiveActivityRecord record : incentiveActivityRecord) { Map activity = new HashMap<>(); @@ -285,14 +1386,16 @@ public Map getAshasAtFacility(Integer supervisorId, Integer faci activity.put("approvalStatus", record.getApprovalStatus()); if(record.getVerifiedByUserId()!=null){ activity.put("verifiedByUserName", userService.getUserDetail(record.getVerifiedByUserId()).getName()); + activity.put("verifiedByUserId", record.getVerifiedByUserId()); + UserServiceRoleDTO roles = userService.getUserDetail(record.getVerifiedByUserId()); + activity.put("role", (roles != null ) ? roles.getRoleName() : null); + } - activity.put("verifiedByUserId", record.getVerifiedByUserId()); activity.put("isClaimed", record.getIsClaimed()); activity.put("claimedDate", record.getCalimedDate()); - UserServiceRoleDTO roles = userService.getUserDetail(record.getVerifiedByUserId()); - activity.put("role", (roles != null ) ? roles.getRoleName() : null); + activityList.add(activity); } @@ -302,34 +1405,111 @@ public Map getAshasAtFacility(Integer supervisorId, Integer faci asha.put("facilityType", facilityType); asha.put("userId", row[0]); asha.put("fullName", fullName(row[1], row[2])); - asha.put("employeeId", str(row[3]).isEmpty() ? null : str(row[3])); + asha.put("employeeId", row[0]); asha.put("mobile", str(row[4]).isEmpty() ? null : str(row[4])); asha.put("gender", str(row[5]).isEmpty() ? null : str(row[5])); asha.put("totalAmount", totalAmount); asha.put("activities", activityList); - long pending = 0, verified = 0, rejected = 0; + + + + if (countList != null && !countList.isEmpty()) { Object[] counts = countList.get(0); - verified = counts[0] != null ? ((Number) counts[0]).longValue() : 0; - pending = counts[1] != null ? ((Number) counts[1]).longValue() : 0; - rejected = counts[2] != null ? ((Number) counts[2]).longValue() : 0; + logger.info("Count List = {}", Arrays.deepToString(countList.toArray())); + logger.info("101={}, 102={}, 103={}, 101+105={}", + counts[0], counts[1], counts[2], counts[3]); + + if(stateCode.equals(StateCode.CG.getStateCode())){ + if(roleName.equalsIgnoreCase("ANM")){ + verified = counts[0] != null ? ((Number) counts[0]).longValue() : 0; + rejected = counts[2] != null ? ((Number) counts[2]).longValue() : 0; + if(approvalStatusID.equals(106)){ + unclaimedCount = counts[1] != null ? ((Number) counts[1]).longValue() : 0; + + }else if(approvalStatusID.equals(0)){ + unclaimedCount = counts[1] != null ? ((Number) counts[1]).longValue() : 0; + pending = counts[3] != null ? ((Number) counts[3]).longValue() : 0; + + }else{ + + pending = counts[3] != null ? ((Number) counts[3]).longValue() : 0; + + } + + + }else if(roleName.equalsIgnoreCase("ASHA Supervisor")) { + verified = counts[0] != null ? ((Number) counts[0]).longValue() : 0; + + if(approvalStatusID.equals(106)){ + unclaimedCount = counts[1] != null ? ((Number) counts[1]).longValue() : 0; + + }else if(approvalStatusID.equals(0)){ + unclaimedCount = counts[1] != null ? ((Number) counts[1]).longValue() : 0; + pending = counts[1] != null ? ((Number) counts[1]).longValue() : 0; + + }else{ + pending = counts[1] != null ? ((Number) counts[1]).longValue() : 0; + + } + rejected = counts[2] != null ? ((Number) counts[2]).longValue() : 0; + + + } + }else { + verified = counts[0] != null ? ((Number) counts[0]).longValue() : 0; + pending = counts[1] != null ? ((Number) counts[1]).longValue() : 0; + rejected = counts[2] != null ? ((Number) counts[2]).longValue() : 0; + } + } - if (verified > 0) overallVerified += 1; - if (rejected > 0) overallRejected += 1; - if (pending > 0) overallPending += 1; + + + if (verified > 0) overallVerified++; + if (rejected > 0) overallRejected++; + if (pending > 0) { + if (isOverDue) overallOverDue++; else overallPending++; + } + if (unclaimedCount > 0) overallUnclaimed++; + + if (unclaimedCount > 0) overallUnclaimed += 1; asha.put("pending", pending); asha.put("verified", verified); asha.put("rejected", rejected); + asha.put("unClaimed", unclaimedCount); int approvalStatus = 0; if (!activityList.isEmpty()) { approvalStatus = (int) activityList.get(0).get("approvalStatus"); } - if (pending == 0 && verified == 0 && rejected == 0) continue; + if ((!approvalStatusID.equals(106) || !facilityId.equals(0))) { + + if (totalAmount == null || totalAmount <= 0) { + logger.info( + "Skipping ASHA {} because totalAmount={}", + ashaId, + totalAmount + ); + continue; + } + } + + logger.info( + "Incentive counts for ASHA ID {} - pending: {}, verified: {}, rejected: {}, unclaimedCount: {}, overallOverDue: {} , approvalStatus: {}", + ashaId, + pending, + verified, + rejected, + unclaimedCount, + overallOverDue,approvalStatus + ); + + if (pending == 0 && verified == 0 && rejected == 0 && unclaimedCount == 0 && overDue==0) continue; + if (approvalStatusID.equals(0)) { asha.put("approvalStatus", approvalStatus); @@ -347,6 +1527,8 @@ public Map getAshasAtFacility(Integer supervisorId, Integer faci approvalStatus.put("verified", overallVerified); approvalStatus.put("pending", overallPending); approvalStatus.put("rejected", overallRejected); + approvalStatus.put("unClaimed", overallUnclaimed); + approvalStatus.put("overDue", overallOverDue); response.put("approvalStatus", approvalStatus); response.put("data", ashaList); @@ -355,6 +1537,31 @@ public Map getAshasAtFacility(Integer supervisorId, Integer faci return response; } + private boolean isAfter24Hours(Timestamp claimedDate) { + if (claimedDate == null) { + return false; + } + + long now = System.currentTimeMillis(); + long diff = now - claimedDate.getTime(); + + logger.info("Now: {}", new Timestamp(now)); + logger.info("ClaimedDate: {}", claimedDate); + logger.info("Diff(ms): {}", diff); + logger.info("Diff(hours): {}", diff / (1000 * 60 * 60.0)); + + return diff >= 24L * 60 * 60 * 1000; + } + private boolean isWithin24Hours(Timestamp claimedDate) { + if (claimedDate == null) { + return false; + } + + long diff = System.currentTimeMillis() - claimedDate.getTime(); + return diff <= 24 * 60 * 60 * 1000L; + } + + private String getGroupNameByState(Integer stateCode) { switch (stateCode) { case 5: @@ -374,6 +1581,8 @@ public int updateApprovalStatus(Integer ashaId, String otherReason, String token) { try { + String title = null; + String body = null; Timestamp approvalDate = Timestamp.valueOf(LocalDateTime.now()); LocalDate startLocalDate = LocalDate.of(year, month, 1); @@ -384,32 +1593,163 @@ public int updateApprovalStatus(Integer ashaId, Integer ashaSupervisorUserId = jwtUtil.extractUserId(token); logger.info("Asha Supervisor User Id : {}", ashaSupervisorUserId); - String ashaSupervisorUsername = userService.getUserDetail(ashaSupervisorUserId).getUserName(); + logger.info("Asha Incentive Id : {}", incentiveIds); + UserServiceRoleDTO ashaSupervisorDetails = userService.getUserDetail(ashaSupervisorUserId); + + int updatedCount = 0; + if(incentiveIds.isEmpty()){ + if (approvalStatus.equals(IncentiveApprovalStatus.REJECTED.getCode())) { + updatedCount = incentiveRecordRepo.updateApprovalStatusById( + approvalStatus, + ashaId, + ashaSupervisorUserId, + ashaSupervisorDetails.getUserName(), + reason, + approvalDate, + otherReason + ); + } else { + + if (ashaSupervisorDetails.getStateId().equals(StateCode.AM.getStateCode())) { + + updatedCount = incentiveRecordRepo.updateApprovalStatusByAshaAndDateRange( + ashaId, approvalStatus, startDate, endDate, + approvalDate, ashaSupervisorUserId, + ashaSupervisorDetails.getUserName()); + + } else if(ashaSupervisorDetails.getStateId().equals(StateCode.CG.getStateCode())){ + + if ("ASHA Supervisor".equalsIgnoreCase(ashaSupervisorDetails.getRoleName())) { + + updatedCount = incentiveRecordRepo.updateApprovalStatusByAshaAndDateRange( + ashaId, 105, startDate, endDate, + approvalDate, ashaSupervisorUserId, + ashaSupervisorDetails.getUserName()); + + } else if("ANM".equalsIgnoreCase(ashaSupervisorDetails.getRoleName())){ + + updatedCount = incentiveRecordRepo.updateApprovalStatusByAshaAndDateRangeForDefaultActivity( + ashaId, approvalStatus, startDate, endDate, + approvalDate, ashaSupervisorUserId, + ashaSupervisorDetails.getUserName()); + + } + } + } + }else { + if (approvalStatus.equals(IncentiveApprovalStatus.REJECTED.getCode())) { + if (incentiveIds != null && !incentiveIds.trim().isEmpty()) { + List ids = Arrays.stream(incentiveIds.split(",")) + .map(String::trim) + .filter(value -> !value.isEmpty()) + .map(Long::valueOf) + .collect(Collectors.toList()); + + updatedCount += incentiveRecordRepo.updateApprovalStatusByIdsAndIncentiveIds( + approvalStatus, + ashaId, + ashaSupervisorUserId, + ashaSupervisorDetails.getUserName(), + reason, + approvalDate, + otherReason, + ids + ); + } + } else { + + if (ashaSupervisorDetails.getStateId().equals(StateCode.AM.getStateCode())) { + if (incentiveIds != null && !incentiveIds.trim().isEmpty()) { + + for (String incentiveId : incentiveIds.split(",")) { - if (approvalStatus.equals(IncentiveApprovalStatus.REJECTED.getCode())) { - int totalUpdated = 0; + Long id = Long.parseLong(incentiveId.trim()); - totalUpdated += incentiveRecordRepo.updateApprovalStatusById( + updatedCount = incentiveRecordRepo.updateApprovalStatusByIncentiveId(id, ashaId, approvalStatus, approvalDate, ashaSupervisorUserId, ashaSupervisorDetails.getUserName()); + + } + } + + + } else if(ashaSupervisorDetails.getStateId().equals(StateCode.CG.getStateCode())){ + + if ("ASHA Supervisor".equalsIgnoreCase(ashaSupervisorDetails.getRoleName())) { + + if (incentiveIds != null && !incentiveIds.trim().isEmpty()) { + + List ids = Arrays.stream(incentiveIds.split(",")) + .map(String::trim) + .filter(value -> !value.isEmpty()) + .map(Long::valueOf) + .collect(Collectors.toList()); + + updatedCount = incentiveRecordRepo.updateApprovalStatusByIncentiveIds(ids, ashaId, 105, approvalDate, ashaSupervisorUserId, ashaSupervisorDetails.getUserName()); + + } + + + } else if("ANM".equalsIgnoreCase(ashaSupervisorDetails.getRoleName())){ + + if (incentiveIds != null && !incentiveIds.trim().isEmpty()) { + List ids = Arrays.stream(incentiveIds.split(",")) + .map(String::trim) + .filter(value -> !value.isEmpty()) + .map(Long::valueOf) + .collect(Collectors.toList()); + + updatedCount = incentiveRecordRepo.updateApprovalStatusByIncentiveIds(ids, + ashaId, approvalStatus, + approvalDate, ashaSupervisorUserId, + ashaSupervisorDetails.getUserName()); + } + + } + } + } + } + + + if (updatedCount > 0) { + sendApprovalNotification( approvalStatus, - ashaSupervisorUserId, - ashaSupervisorUsername, + ashaId, + month, + year, reason, - approvalDate, - otherReason + otherReason, + ashaSupervisorUserId, + ashaSupervisorDetails ); - return totalUpdated; + boolean isApproved = !approvalStatus.equals(IncentiveApprovalStatus.REJECTED.getCode()); + + // Final approval: AM mein supervisor/CHO, CG mein sirf ANM + // (CG mein ASHA Supervisor ka status 105 intermediate hai, uspar payment nahi) + boolean isFinalApprover = + ashaSupervisorDetails.getStateId().equals(StateCode.AM.getStateCode()) + || (ashaSupervisorDetails.getStateId().equals(StateCode.CG.getStateCode()) + && "ANM".equalsIgnoreCase(ashaSupervisorDetails.getRoleName())); + + if (isApproved && isFinalApprover) { + List approvedRecords; + + if (incentiveIds == null || incentiveIds.trim().isEmpty()) { + approvedRecords = incentiveRecordRepo + .findApprovedForMonth( + ashaId, approvalStatus); + } else { + List ids = Arrays.stream(incentiveIds.split(",")) + .map(String::trim).filter(v -> !v.isEmpty()) + .map(Long::valueOf).collect(Collectors.toList()); + approvedRecords = incentiveRecordRepo.findAllById(ids); + } + + triggerPayment(ashaId, month, year, approvedRecords, + ashaSupervisorDetails, ashaSupervisorUserId); + } } - return incentiveRecordRepo.updateApprovalStatusByAshaAndDateRange( - ashaId, - approvalStatus, - startDate, - endDate, - approvalDate, - ashaSupervisorUserId, - ashaSupervisorUsername - ); + return updatedCount; } catch (Exception e) { logger.error("Update claim :" + e.getMessage()); @@ -418,6 +1758,195 @@ public int updateApprovalStatus(Integer ashaId, } } + + private void sendApprovalNotification( + Integer approvalStatus, + Integer ashaId, + Integer month, + Integer year, + String reason, + String otherReason, + Integer supervisorId, + UserServiceRoleDTO supervisor) { + + String title; + String body; + + if (approvalStatus.equals(IncentiveApprovalStatus.REJECTED.getCode())) { + + title = "Incentive Rejected"; + + body = "Incentive claim for " + Month.of(month).name() + " " + year + " has been rejected."; + + notificationService.sendNotification( + "FLW", "NA", + title, + body, + "INCENTIVE_REJECTED", + "INCENTIVE", + supervisorId); + + body = supervisor.getName() + " has rejected your incentive claim for " + + Month.of(month).name() + " " + year + "due to "+reason+" "+otherReason; + + notificationService.sendNotification( + "FLW", "NA", + title, + body, + "INCENTIVE_REJECTED", + "INCENTIVE", + ashaId); + + } else { + + title = "Incentive Approved"; + + body = "Incentive claim for " + Month.of(month).name() + " " + year + " has been approved."; + + notificationService.sendNotification( + "FLW", "NA", + title, + body, + "INCENTIVE_CLAIMED", + "INCENTIVE", + supervisorId); + + body = supervisor.getName() + " has approved your incentive claim for " + + Month.of(month).name() + " " + year; + + notificationService.sendNotification( + "FLW", "NA", + title, + body, + "INCENTIVE_CLAIMED", + "INCENTIVE", + ashaId); + } + } + + public void triggerPayment(Integer ashaId, Integer month, Integer year, + List approvedRecords, + UserServiceRoleDTO supervisor, Integer supervisorUserId) { + + logger.info("========================================"); + logger.info("SSD Request Send to portal"); + logger.info("========================================"); + + try { + if (approvedRecords == null || approvedRecords.isEmpty()) { + logger.info("No approved records, payment not triggered for asha {}", ashaId); + return; + } + + LocalDate first = LocalDate.of(year, month, 1); + + Period period = new Period(); + period.setStart(first.toString()); + period.setEnd(first.withDayOfMonth(first.lengthOfMonth()).toString()); + + VerifiedBy verifiedBy = new VerifiedBy(); + verifiedBy.setEmployeeId("NRHM-22547"); + verifiedBy.setName(supervisor.getUserName()); + + Map> byActivity = + approvedRecords.stream() + .collect(Collectors.groupingBy( + IncentiveActivityRecord::getActivityId + )); + + List items = new ArrayList<>(); + + byActivity.forEach((activityId, recs) -> { + + var activityOpt = incentivesRepo.findById(activityId); + + // Activity not found -> skip + if (activityOpt.isEmpty()) { + logger.warn( + "Activity not found, skipping payment item. activityId={}", + activityId + ); + return; + } + + var activity = activityOpt.get(); + + // State Activity Code null -> skip + if (activity.getStateActivityCode() == null) { + logger.warn( + "State Activity Code is null, skipping payment item. activityId={}", + activityId + ); + return; + } + + long count = recs.size(); + + long total = recs.stream() + .mapToLong(r -> r.getAmount() == null ? 0L : r.getAmount()) + .sum(); + + PaymentItem item = new PaymentItem(); + + item.setActivityCode( + String.valueOf(activity.getStateActivityCode()) + ); + + item.setCount(String.valueOf(count)); + item.setIncentiveAmount(String.valueOf(total)); + + items.add(item); + }); + + // Agar saare records ka activity code null tha + if (items.isEmpty()) { + logger.info( + "No valid payment items found for asha {}, payment not triggered", + ashaId + ); + return; + } + + String timestamp = OffsetDateTime.now(ZoneId.of("Asia/Kolkata")) + .truncatedTo(ChronoUnit.SECONDS) + .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + + if (employeeMasterRepo.findUserByUserID(ashaId) != null) { + + String empId = employeeMasterRepo + .findUserByUserID(ashaId) + .getEmployeeID(); + + if (empId != null) { + + PaymentRequest paymentRequest = new PaymentRequest( + UUID.randomUUID().toString(), + "AMRIT", + period, + String.valueOf(30638), + timestamp, + verifiedBy, + items + ); + + logger.info( + "PAYMENT REQUEST Payload: {}", + new Gson().toJson(paymentRequest) + ); + + paymentService.sendPaymentRequest(paymentRequest); + } + } + + } catch (Exception e) { + logger.error( + "Payment request failed for asha {}: {}", + ashaId, + e.getMessage(), + e + ); + } + } private JSONObject buildEmptyIncentiveSummary() { JSONObject summary = new JSONObject(); summary.put("verified", 0); diff --git a/src/main/java/com/iemr/flw/service/impl/TBConfirmedCaseServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/TBConfirmedCaseServiceImpl.java index d5018cf09..9290733d5 100644 --- a/src/main/java/com/iemr/flw/service/impl/TBConfirmedCaseServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/TBConfirmedCaseServiceImpl.java @@ -1,5 +1,14 @@ package com.iemr.flw.service.impl; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iemr.flw.domain.iemr.TbReferralFollowUp; +import com.iemr.flw.domain.iemr.TbTptFollowUp; +import com.iemr.flw.dto.iemr.TbReferralFollowUpDTO; +import com.iemr.flw.dto.iemr.TbReferralFollowUpListDTO; +import com.iemr.flw.dto.iemr.TbTptFollowUpDTO; +import com.iemr.flw.dto.iemr.TbTptFollowUpListDTO; +import com.iemr.flw.repo.iemr.TbReferralFollowUpRepo; +import com.iemr.flw.repo.iemr.TbTptFollowUpRepo; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.iemr.flw.domain.iemr.DynamicForm; @@ -25,12 +34,14 @@ import java.sql.Timestamp; import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -59,6 +70,15 @@ public class TBConfirmedCaseServiceImpl implements TBConfirmedCaseService { @Autowired private BeneficiaryRepo beneficiaryRepo; + @Autowired + private TbReferralFollowUpRepo tbReferralFollowUpRepo; + + @Autowired + private IncentiveLogicImpl incentiveLogic; + + @Autowired + private TbTptFollowUpRepo tbTptFollowUpRepo; + public TBConfirmedCaseServiceImpl(TBConfirmedTreatmentRepository repository, FormResponseRepo formResponseRepo, DynamicFormRepo dynamicFormRepo) { @@ -207,9 +227,448 @@ public String getByProviderServiceMapId(Integer providerServiceMapID, Integer vi return buildTbConfirmedCasesResponse(null, list); } - private String buildTbConfirmedCasesResponse(Integer userId, List list) { - List dtoList = list.stream().map(this::toDTO).collect(Collectors.toList()); + @Override + public Map getRecordsByIds(java.util.Collection ids) { + List intIds = ids.stream().map(Long::intValue).collect(Collectors.toList()); + List dtoList = withCounselled( + repository.findAllById(intIds).stream().map(this::toDTO).collect(Collectors.toList())); + Gson gson = confirmedGson(); + Map out = new java.util.LinkedHashMap<>(); + for (TBConfirmedCaseDTO dto : dtoList) out.put(dto.getId().longValue(), gson.toJson(dto)); + return out; + } + + @Override + public String saveReferralFollowUp(TbReferralFollowUpDTO requestDTO, String token) { + OutputResponse response = new OutputResponse(); + + try { + + TbReferralFollowUp tbReferralFollowUp = new TbReferralFollowUp(); + + if(!token.isEmpty() && token!=null && requestDTO!=null){ + String userName = jwtUtil.extractUsername(token); + Integer userId = jwtUtil.extractUserId(token); + tbReferralFollowUp.setBenId(requestDTO.getBenId()); + tbReferralFollowUp.setHouseHoldId(requestDTO.getHouseHoldId()); + tbReferralFollowUp.setSyncedBy(userName); + tbReferralFollowUp.setUpdatedBy(userName); + tbReferralFollowUp.setCreatedBy(userName); + tbReferralFollowUp.setUserId(userId); + if(requestDTO.getFields().getReferredOnDate()!=null && !requestDTO.getFields().getFollowUpDate().isEmpty()){ + Timestamp referOnDate = Timestamp.valueOf(requestDTO.getFields().getReferredOnDate()); + tbReferralFollowUp.setFollowUpDate(referOnDate); + + } + + if(requestDTO.getFields().getFollowUpDate()!=null && !requestDTO.getFields().getFollowUpDate().isEmpty()){ + Timestamp followUpDate = Timestamp.valueOf(requestDTO.getFields().getFollowUpDate()); + tbReferralFollowUp.setFollowUpDate(followUpDate); + + } + tbReferralFollowUp.setFollowUpStatus(requestDTO.getFields().getFollowUpStatus()); + } + + tbReferralFollowUpRepo.save(tbReferralFollowUp); + response.setResponse("TB Referral follow up saved successfully"); + + + + }catch (Exception e){ + response.setError(500,e.getMessage()); + return response.toString(); + } + + + return response.toString(); + } + + @Override + public String getReferralFollowUp(String token) { + OutputResponse response = new OutputResponse(); + + try { + if (token == null || token.trim().isEmpty()) { + response.setError(400, "Token is required"); + return response.toString(); + } + + Integer userId = jwtUtil.extractUserId(token); + + List referrals = + tbReferralFollowUpRepo.findByUserId(userId); + + List referralDTOs = new ArrayList<>(); + + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + for (TbReferralFollowUp referral : referrals) { + + TbReferralFollowUpDTO dto = new TbReferralFollowUpDTO(); + dto.setBenId(referral.getBenId()); + dto.setUserId(referral.getUserId()); + dto.setHouseHoldId(referral.getHouseHoldId()); + + // Initialize nested DTO before setting fields + TbReferralFollowUpListDTO fields = + new TbReferralFollowUpListDTO(); + + if (referral.getReferredOnDate() != null) { + fields.setReferredOnDate( + referral.getReferredOnDate() + .toLocalDateTime() + .format(formatter) + ); + } + + if (referral.getFollowUpDate() != null) { + fields.setFollowUpDate( + referral.getFollowUpDate() + .toLocalDateTime() + .format(formatter) + ); + } + + fields.setFollowUpStatus(referral.getFollowUpStatus()); + + dto.setFields(fields); + referralDTOs.add(dto); + } + ObjectMapper objectMapper = new ObjectMapper(); + + response.setResponse( + objectMapper.writeValueAsString(referralDTOs) + ); + + } catch (Exception e) { + // Replace with your project's logger if available + e.printStackTrace(); + response.setError(500, "Failed to fetch referral follow-up: " + e.getMessage()); + } + + return response.toString(); + } + + @Override + public String saveTptFollowUp(TbTptFollowUpDTO requestDTO, String token) { + + OutputResponse response = new OutputResponse(); + + try { + + TbTptFollowUp tbTptFollowUp = new TbTptFollowUp(); + + if (token != null && !token.isEmpty() && requestDTO != null) { + + String userName = jwtUtil.extractUsername(token); + Integer userId = jwtUtil.extractUserId(token); + + tbTptFollowUp.setBenId(requestDTO.getBenId()); + tbTptFollowUp.setUserId(userId); + + tbTptFollowUp.setCreatedBy(userName); + tbTptFollowUp.setUpdatedBy(userName); + + if (requestDTO.getFields() != null) { + + TbTptFollowUpListDTO fields = requestDTO.getFields(); + + tbTptFollowUp.setRegimenType(fields.getRegimenType()); + + if (fields.getTreatmentStartDate() != null + && !fields.getTreatmentStartDate().isEmpty()) { + + tbTptFollowUp.setTreatmentStartDate( + Timestamp.valueOf(fields.getTreatmentStartDate()) + ); + } + + if (fields.getExpectedTreatmentCompletionDate() != null + && !fields.getExpectedTreatmentCompletionDate().isEmpty()) { + + tbTptFollowUp.setExpectedTreatmentCompletionDate( + Timestamp.valueOf(fields.getExpectedTreatmentCompletionDate()) + ); + } + + if (fields.getFollowUpDate() != null + && !fields.getFollowUpDate().isEmpty()) { + + tbTptFollowUp.setFollowUpDate( + Timestamp.valueOf(fields.getFollowUpDate()) + ); + } + + tbTptFollowUp.setFollowUpMonth( + fields.getMonthlyFollowUp() + ); + + tbTptFollowUp.setAdherenceToMedicines( + fields.getMedicineAdherence() + ); + + tbTptFollowUp.setAnyDiscomfort( + convertBollen(fields.getAnyDiscomfort()) + ); + + tbTptFollowUp.setTreatmentCompleted( + convertBollen(fields.getTreatmentCompleted()) + ); + + if (fields.getActualCompletionDate() != null + && !fields.getActualCompletionDate().isEmpty()) { + + tbTptFollowUp.setActualTreatmentCompletionDate( + Timestamp.valueOf(fields.getActualCompletionDate()) + ); + } + + tbTptFollowUp.setTptOutcome( + fields.getTptOutcome() + ); + + if ("Death".equalsIgnoreCase(fields.getTptOutcome())) { + + if (fields.getDateOfDeath() != null + && !fields.getDateOfDeath().isEmpty()) { + + tbTptFollowUp.setDateOfDeath( + Timestamp.valueOf(fields.getDateOfDeath()) + ); + } + + tbTptFollowUp.setPlaceOfDeath( + fields.getPlaceOfDeath() + ); + + tbTptFollowUp.setReasonForDeath( + fields.getReasonForDeath() + ); + } + } + + tbTptFollowUpRepo.save(tbTptFollowUp); + + createTptIncentiveIfEligible(tbTptFollowUp); + } + + response.setResponse("TPT follow up saved successfully"); + + } catch (Exception e) { + + response.setError(500, e.getMessage()); + return response.toString(); + } + + return response.toString(); + } + + + private static final Map MIN_FOLLOW_UPS = Map.of( + "1HP", 1, + "3HP", 3, + "3RH", 3, + "4R", 4, + "6H", 6, + "6LFX", 6 + ); + + + private void createTptIncentiveIfEligible(TbTptFollowUp saved) { + + // 1. Basic conditions + if (!Boolean.TRUE.equals(saved.getTreatmentCompleted()) + || saved.getActualTreatmentCompletionDate() == null + || saved.getRegimenType() == null + || saved.getTreatmentStartDate() == null + || saved.getBenId() == null) { + return; + } + + // 2. Regimen ka minimum follow-up count + Integer minRequired = MIN_FOLLOW_UPS.get(saved.getRegimenType().trim().toUpperCase()); + if (minRequired == null) { + return; // unknown regimen + } + + List cycleRows = tbTptFollowUpRepo + .findByBenIdAndRegimenTypeAndTreatmentStartDate( + saved.getBenId(), + saved.getRegimenType(), + saved.getTreatmentStartDate()); + + long completedMonths = cycleRows.stream() + .map(TbTptFollowUp::getFollowUpMonth) + .filter(Objects::nonNull) + .map(String::trim) + .distinct() + .count(); + + if (completedMonths < minRequired) { + return; + } + Timestamp completionDate = saved.getActualTreatmentCompletionDate(); + + incentiveLogic.incentiveForTbPreventiveFollowUp( + saved.getBenId(), + completionDate, + completionDate, + saved.getUserId()); + } + + @Override + public String getTptFollowUp(String token) { + + OutputResponse response = new OutputResponse(); + + try { + + if (token == null || token.trim().isEmpty()) { + response.setError(400, "Token is required"); + return response.toString(); + } + + Integer userId = jwtUtil.extractUserId(token); + + List tptFollowUps = + tbTptFollowUpRepo.findByUserId(userId); + + List tptFollowUpDTOs = + new ArrayList<>(); + + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + for (TbTptFollowUp tptFollowUp : tptFollowUps) { + + TbTptFollowUpDTO dto = new TbTptFollowUpDTO(); + + dto.setBenId(tptFollowUp.getBenId()); + dto.setUserId(tptFollowUp.getUserId()); + + TbTptFollowUpListDTO fields = + new TbTptFollowUpListDTO(); + + // Regimen Type + fields.setRegimenType( + tptFollowUp.getRegimenType() + ); + + // Treatment Start Date + if (tptFollowUp.getTreatmentStartDate() != null) { + fields.setTreatmentStartDate( + tptFollowUp.getTreatmentStartDate() + .toLocalDateTime() + .format(formatter) + ); + } + + // Expected Treatment Completion Date + if (tptFollowUp.getExpectedTreatmentCompletionDate() != null) { + fields.setExpectedTreatmentCompletionDate( + tptFollowUp.getExpectedTreatmentCompletionDate() + .toLocalDateTime() + .format(formatter) + ); + } + + // Follow Up Date + if (tptFollowUp.getFollowUpDate() != null) { + fields.setFollowUpDate( + tptFollowUp.getFollowUpDate() + .toLocalDateTime() + .format(formatter) + ); + } + + // Monthly Follow Up + fields.setMonthlyFollowUp( + tptFollowUp.getFollowUpMonth() + ); + + // Medicine Adherence + fields.setMedicineAdherence( + tptFollowUp.getAdherenceToMedicines() + ); + + // Any Discomfort + fields.setAnyDiscomfort( + convert(tptFollowUp.getAnyDiscomfort()) + ); + + // Treatment Completed + fields.setTreatmentCompleted( + convert(tptFollowUp.getTreatmentCompleted()) + ); + + // Actual Completion Date + if (tptFollowUp.getActualTreatmentCompletionDate() != null) { + fields.setActualCompletionDate( + tptFollowUp.getActualTreatmentCompletionDate() + .toLocalDateTime() + .format(formatter) + ); + } + + // TPT Outcome + fields.setTptOutcome( + tptFollowUp.getTptOutcome() + ); + + // Date of Death + if (tptFollowUp.getDateOfDeath() != null) { + fields.setDateOfDeath( + tptFollowUp.getDateOfDeath() + .toLocalDateTime() + .format(formatter) + ); + } + + // Place of Death + fields.setPlaceOfDeath( + tptFollowUp.getPlaceOfDeath() + ); + + // Reason for Death + fields.setReasonForDeath( + tptFollowUp.getReasonForDeath() + ); + + dto.setFields(fields); + + tptFollowUpDTOs.add(dto); + } + + ObjectMapper objectMapper = new ObjectMapper(); + + response.setResponse( + objectMapper.writeValueAsString(tptFollowUpDTOs) + ); + + } catch (Exception e) { + + e.printStackTrace(); + + response.setError( + 500, + "Failed to fetch TPT follow-up: " + e.getMessage() + ); + } + + return response.toString(); + } + + + private Gson confirmedGson() { + return new GsonBuilder() + .registerTypeAdapter(LocalDate.class, new LocalDateAdapter()) + .setDateFormat("MMM dd, yyyy h:mm:ss a") + .create(); + } + + private List withCounselled(List dtoList) { List benIds = dtoList.stream().map(TBConfirmedCaseDTO::getBenId).collect(Collectors.toList()); Set counselledBenIds = Collections.emptySet(); if (!benIds.isEmpty()) { @@ -220,15 +679,16 @@ private String buildTbConfirmedCasesResponse(Integer userId, List counselled = counselledBenIds; dtoList.forEach(dto -> dto.setCounselled(counselled.contains(dto.getBenId()))); + return dtoList; + } + + private String buildTbConfirmedCasesResponse(Integer userId, List list) { + List dtoList = withCounselled(list.stream().map(this::toDTO).collect(Collectors.toList())); Map response = new java.util.HashMap<>(); response.put("userId", userId); response.put("tbConfirmedCases", dtoList); - Gson gson = new GsonBuilder() - .registerTypeAdapter(LocalDate.class, new LocalDateAdapter()) - .setDateFormat("MMM dd, yyyy h:mm:ss a") - .create(); - return gson.toJson(response); + return confirmedGson().toJson(response); } // Utility: convert entity -> DTO @@ -273,4 +733,17 @@ private List splitSideEffects(String sideEffects) { .filter(s -> !s.isEmpty()) .collect(Collectors.toList()); } + + private String convert(Boolean value) { + if (value == null) return null; + return value ? "Yes" : "No"; + } + + private Boolean convertBollen(String value) { + if (value != null && !value.isEmpty()) { + return value.equalsIgnoreCase("Yes"); + } else { + return false; + } + } } diff --git a/src/main/java/com/iemr/flw/service/impl/TBScreeningServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/TBScreeningServiceImpl.java index 2f84c4622..adf3e0d1b 100644 --- a/src/main/java/com/iemr/flw/service/impl/TBScreeningServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/TBScreeningServiceImpl.java @@ -9,7 +9,9 @@ import com.iemr.flw.repo.identity.BeneficiaryRepo; import com.iemr.flw.repo.iemr.TBScreeningRepo; import com.iemr.flw.service.TBScreeningService; +import jakarta.annotation.PostConstruct; import org.modelmapper.ModelMapper; +import org.modelmapper.TypeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -17,7 +19,9 @@ import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; @Service public class TBScreeningServiceImpl implements TBScreeningService { @@ -36,56 +40,195 @@ public String getByBenId(Long benId, String authorisation) throws Exception { @Override public String save(TBScreeningRequestDTO requestDTO) throws Exception { - requestDTO.getTbScreeningList().forEach(tbScreeningDTO -> - { + if (requestDTO == null || requestDTO.getTbScreeningList() == null) { + throw new IllegalArgumentException("TB screening list is required"); + } + + for (TBScreeningDTO dto : requestDTO.getTbScreeningList()) { TBScreening tbScreening = - tbScreeningRepo.getByUserIdAndBenId(tbScreeningDTO.getBenId(), requestDTO.getUserId()); + tbScreeningRepo.getByUserIdAndBenId( + dto.getBenId(), requestDTO.getUserId()); boolean isNew = tbScreening == null; + if (isNew) { tbScreening = new TBScreening(); - modelMapper.map(tbScreeningDTO, tbScreening); - tbScreening.setId(null); - } else { - Long id = tbScreening.getId(); - modelMapper.map(tbScreeningDTO, tbScreening); - tbScreening.setId(id); } + modelMapper.map(dto, tbScreening); + + // List -> comma-separated String + tbScreening.setKeyPopulationRiskFactorIds( + toRiskFactorIdsCsv(dto.getKeyPopulationRiskFactorIds())); + + // List -> comma-separated String + tbScreening.setKeyPopulationRiskFactors( + toRiskFactorsCsv(dto.getKeyPopulationRiskFactors())); + tbScreening.setUserId(requestDTO.getUserId()); - // Stop TB / Nikshay reporting — same gap this ASHA-flow save path had as the nurse - // flow's saveNurseTBScreening(): benRegID was never persisted here at all. - tbScreening.setBenRegID(beneficiaryRepo.getRegIDFromBenId(tbScreeningDTO.getBenId())); - // created_date/created_by — gated on isNew so a later re-save doesn't overwrite the - // true creation time/author. No device-supplied created timestamp on this DTO, so - // visitDate (closest real proxy) is preferred, server time as last-resort fallback. + + tbScreening.setBenRegID( + beneficiaryRepo.getRegIDFromBenId(dto.getBenId())); + if (isNew) { - tbScreening.setCreatedDate(tbScreening.getVisitDate() != null - ? tbScreening.getVisitDate() : new Timestamp(System.currentTimeMillis())); - tbScreening.setCreatedBy(requestDTO.getUserId() != null ? requestDTO.getUserId().toString() : null); + tbScreening.setId(null); + + tbScreening.setCreatedDate( + tbScreening.getVisitDate() != null + ? tbScreening.getVisitDate() + : new Timestamp(System.currentTimeMillis())); + + tbScreening.setCreatedBy( + requestDTO.getUserId() != null + ? requestDTO.getUserId().toString() + : null); } + tbScreeningRepo.save(tbScreening); - }); - return "no of tb screening items saved: " + requestDTO.getTbScreeningList().size(); + } + + return "no of tb screening items saved: " + + requestDTO.getTbScreeningList().size(); } @Override public String getByUserId(GetBenRequestHandler request) { try { List dtos = new ArrayList<>(); - List tbScreeningList = tbScreeningRepo.getByUserId(request.getAshaId()); - tbScreeningList.forEach(tbScreening -> dtos.add(modelMapper.map(tbScreening, TBScreeningDTO.class))); - TBScreeningRequestDTO tbScreeningRequestDTO = new TBScreeningRequestDTO(); - tbScreeningRequestDTO.setTbScreeningList(dtos); - tbScreeningRequestDTO.setUserId(request.getAshaId()); + + List tbScreeningList = + tbScreeningRepo.getByUserId(request.getAshaId()); + + for (TBScreening tbScreening : tbScreeningList) { + TBScreeningDTO dto = + modelMapper.map(tbScreening, TBScreeningDTO.class); + + // Comma-separated String -> List + dto.setKeyPopulationRiskFactorIds( + fromRiskFactorIdsCsv( + tbScreening.getKeyPopulationRiskFactorIds())); + + // Comma-separated String -> List + dto.setKeyPopulationRiskFactors( + fromRiskFactorsCsv( + tbScreening.getKeyPopulationRiskFactors())); + + dtos.add(dto); + } + + TBScreeningRequestDTO response = new TBScreeningRequestDTO(); + response.setTbScreeningList(dtos); + response.setUserId(request.getAshaId()); + Gson gson = new GsonBuilder() - .setDateFormat("MMM dd, yyyy h:mm:ss a") // Set the desired date format + .setDateFormat("MMM dd, yyyy h:mm:ss a") .create(); - return gson.toJson(tbScreeningRequestDTO); - }catch (Exception e){ - logger.error("TBScreening Exception:"+e.getMessage()); + + + + String responseJson = gson.toJson(response); + + // Print complete response + logger.info("TB Screening COMPLETE RESPONSE: {}", responseJson); + + return gson.toJson(response); + + } catch (Exception e) { + logger.error("Failed to retrieve TB screening details", e); + throw new IllegalStateException( + "Failed to retrieve TB screening details", e); } - return null; + } + + @PostConstruct + public void configureTBScreeningMappings() { + TypeMap saveMapping = + modelMapper.getTypeMap(TBScreeningDTO.class, TBScreening.class); + + if (saveMapping == null) { + saveMapping = modelMapper.createTypeMap( + TBScreeningDTO.class, TBScreening.class); + } + + saveMapping.addMappings(mapper -> { + mapper.skip(TBScreening::setKeyPopulationRiskFactorIds); + mapper.skip(TBScreening::setKeyPopulationRiskFactors); + + // Managed by the service; preserve these on updates. + mapper.skip(TBScreening::setId); + mapper.skip(TBScreening::setCreatedDate); + mapper.skip(TBScreening::setCreatedBy); + }); + + TypeMap responseMapping = + modelMapper.getTypeMap(TBScreening.class, TBScreeningDTO.class); + + if (responseMapping == null) { + responseMapping = modelMapper.createTypeMap( + TBScreening.class, TBScreeningDTO.class); + } + + responseMapping.addMappings(mapper -> { + mapper.skip(TBScreeningDTO::setKeyPopulationRiskFactorIds); + mapper.skip(TBScreeningDTO::setKeyPopulationRiskFactors); + }); + } + + private String toRiskFactorIdsCsv(List ids) { + if (ids == null) { + return null; + } + + if (ids.stream().anyMatch(id -> id == null)) { + throw new IllegalArgumentException( + "Risk factor IDs cannot contain null values"); + } + + return ids.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + } + + private String toRiskFactorsCsv(List riskFactors) { + if (riskFactors == null) { + return null; + } + + for (String riskFactor : riskFactors) { + if (riskFactor == null + || riskFactor.isBlank() + || riskFactor.contains(",")) { + throw new IllegalArgumentException( + "Risk factor codes cannot be null, blank, or contain commas"); + } + } + + return riskFactors.stream() + .map(String::trim) + .collect(Collectors.joining(",")); + } + + private List fromRiskFactorIdsCsv(String value) { + if (value == null || value.isBlank()) { + return new ArrayList<>(); + } + + return Arrays.stream(value.split(",")) + .map(String::trim) + .filter(item -> !item.isEmpty()) + .map(Integer::valueOf) + .collect(Collectors.toList()); + } + + private List fromRiskFactorsCsv(String value) { + if (value == null || value.isBlank()) { + return new ArrayList<>(); + } + + return Arrays.stream(value.split(",")) + .map(String::trim) + .filter(item -> !item.isEmpty()) + .collect(Collectors.toList()); } } diff --git a/src/main/java/com/iemr/flw/service/impl/TBStopVisitServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/TBStopVisitServiceImpl.java index 7297adfc1..d3f3e485f 100644 --- a/src/main/java/com/iemr/flw/service/impl/TBStopVisitServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/TBStopVisitServiceImpl.java @@ -25,7 +25,17 @@ public BenVisitDetail getOrCreateVisitForToday(Long beneficiaryRegID, Integer pr Timestamp dayEnd = Timestamp.valueOf(today.plusDays(1).atStartOfDay()); BenVisitDetail existing = benVisitDetailsRepo.findStopTBVisitForToday(beneficiaryRegID, dayStart, dayEnd); - if (existing != null) return existing; + if (existing != null) { + // TB suspected / confirmed / diagnostic saves create the visit without a service line; whichever + // form syncs first decides. Fill it from the first later save that has one (screening, OPD...). + if (existing.getProviderServiceMapID() == null && providerServiceMapID != null) { + existing.setProviderServiceMapID(providerServiceMapID); + existing.setModifiedBy(createdBy); + existing.setProcessed("N"); + existing = benVisitDetailsRepo.saveAndFlush(existing); + } + return existing; + } Integer visitCount = benVisitDetailsRepo.countStopTBVisits(beneficiaryRegID); short visitNo = (short) ((visitCount != null ? visitCount : 0) + 1); diff --git a/src/main/java/com/iemr/flw/service/impl/TBSuspectedServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/TBSuspectedServiceImpl.java index 99db4fcbe..1f8fa0b8c 100644 --- a/src/main/java/com/iemr/flw/service/impl/TBSuspectedServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/TBSuspectedServiceImpl.java @@ -13,6 +13,8 @@ import com.iemr.flw.service.CampConfigService; import com.iemr.flw.service.TBStopVisitService; import com.iemr.flw.service.TBSuspectedService; +import com.iemr.flw.service.UserService; +import com.iemr.flw.masterEnum.StateCode; import org.modelmapper.ModelMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +23,11 @@ import java.sql.Timestamp; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; @Service public class TBSuspectedServiceImpl implements TBSuspectedService { @@ -40,6 +46,9 @@ public class TBSuspectedServiceImpl implements TBSuspectedService { @Autowired private IncentiveLogicService incentiveLogicService; + @Autowired + private UserService userService; + @Override public String getByBenId(Long benId, String authorisation) throws Exception { return null; @@ -140,16 +149,115 @@ public String save(TBSuspectedRequestDTO requestDTO) throws Exception { @Override public String getByUserId(GetBenRequestHandler request) { List dtos = new ArrayList<>(); - List tbSuspectedList = request.getProviderServiceMapID() != null + boolean stopTbVillage = request.getProviderServiceMapID() != null; + List tbSuspectedList = stopTbVillage ? tbSuspectedRepo.getByProviderServiceMapIdAndVillageId(request.getProviderServiceMapID(), request.getVillageID()) : tbSuspectedRepo.getByUserId(request.getAshaId(), request.getFromDate(), request.getToDate()); + if (stopTbVillage) { + dtos = stopTbVillageDtos(tbSuspectedList); + } else { + for (TBSuspected tbSuspected : tbSuspectedList) { + TBSuspectedDTO dto = modelMapper.map(tbSuspected, TBSuspectedDTO.class); + dto.setUpdateDate(tbSuspected.getLastModDate()); + dto.setUpdatedBy(tbSuspected.getModifiedBy()); + dtos.add(dto); + + if (tbSuspected != null && Boolean.TRUE.equals(tbSuspected.getIsConfirmed())) { + incentiveLogicService.incentiveForTbSuspected( + tbSuspected.getBenId(), + tbSuspected.getVisitDate(), + tbSuspected.getVisitDate(), + tbSuspected.getUserId() + ); + } + } + } + TBSuspectedRequestDTO tbSuspectedRequestDTO = new TBSuspectedRequestDTO(); + tbSuspectedRequestDTO.setTbSuspectedList(dtos); + tbSuspectedRequestDTO.setUserId(request.getAshaId()); + Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); + return gson.toJson(tbSuspectedRequestDTO); + } + + // Stop TB village list (psm + village). Same DTOs and incentive calls as the per-user path, but the + // user's state is read once per user: IncentiveLogicImpl.incentiveForTbSuspected loads the full user + // detail on every call only to check for CG, and does nothing for any other state, so for other + // states the call is skipped and for CG users it is made exactly as before. + private List stopTbVillageDtos(List tbSuspectedList) { + List dtos = new ArrayList<>(); + Map stateByUser = new HashMap<>(); + Set usersWithoutDetail = new HashSet<>(); for (TBSuspected tbSuspected : tbSuspectedList) { - TBSuspectedDTO dto = modelMapper.map(tbSuspected, TBSuspectedDTO.class); + TBSuspectedDTO dto = toDto(tbSuspected); dto.setUpdateDate(tbSuspected.getLastModDate()); dto.setUpdatedBy(tbSuspected.getModifiedBy()); dtos.add(dto); - if (tbSuspected != null && Boolean.TRUE.equals(tbSuspected.getIsConfirmed())) { + if (Boolean.TRUE.equals(tbSuspected.getIsConfirmed())) { + Integer userId = tbSuspected.getUserId(); + if (!stateByUser.containsKey(userId) && !usersWithoutDetail.contains(userId)) { + try { + stateByUser.put(userId, userService.getUserDetail(userId).getStateId()); + } catch (Exception e) { + logger.error("Check Tb Suspected Incentive Exception: ", e); + usersWithoutDetail.add(userId); + } + } + Integer stateCode = stateByUser.get(userId); + if (stateCode != null && stateCode == StateCode.CG.getStateCode()) { + incentiveLogicService.incentiveForTbSuspected( + tbSuspected.getBenId(), + tbSuspected.getVisitDate(), + tbSuspected.getVisitDate(), + userId + ); + } + } + } + return dtos; + } + + // Field-by-field copy of the same-named fields ModelMapper copied (updateDate/updatedBy are set by + // the caller); ModelMapper's reflection per row was most of this list's time at village scale. + private static TBSuspectedDTO toDto(TBSuspected s) { + TBSuspectedDTO dto = new TBSuspectedDTO(); + dto.setId(s.getId()); + dto.setBenId(s.getBenId()); + dto.setVisitLabel(s.getVisitLabel()); + dto.setVisitDate(s.getVisitDate()); + dto.setTypeOfTBCase(s.getTypeOfTBCase()); + dto.setReasonForSuspicion(s.getReasonForSuspicion()); + dto.setIsSputumCollected(s.getIsSputumCollected()); + dto.setSputumSubmittedAt(s.getSputumSubmittedAt()); + dto.setNikshayId(s.getNikshayId()); + dto.setSputumTestResult(s.getSputumTestResult()); + dto.setIsChestXRayDone(s.getIsChestXRayDone()); + dto.setChestXRayResult(s.getChestXRayResult()); + dto.setReasonForRefusalXray(s.getReasonForRefusalXray()); + dto.setReasonForRefusalMTB(s.getReasonForRefusalMTB()); + dto.setReasonForRefusalMDRRIF(s.getReasonForRefusalMDRRIF()); + dto.setReasonForRefusalSputum(s.getReasonForRefusalSputum()); + dto.setMdrRifResult(s.getMdrRifResult()); + dto.setReferralFacility(s.getReferralFacility()); + dto.setIsTBConfirmed(s.getIsTBConfirmed()); + dto.setIsConfirmed(s.getIsConfirmed()); + dto.setIsDRTBConfirmed(s.getIsDRTBConfirmed()); + dto.setReferred(s.getReferred()); + dto.setFollowUps(s.getFollowUps()); + return dto; + } + + @Override + public java.util.Map getRecordsByIds(java.util.Collection ids) { + Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); + java.util.Map out = new java.util.LinkedHashMap<>(); + for (TBSuspected tbSuspected : tbSuspectedRepo.findAllById(ids)) { + TBSuspectedDTO dto = modelMapper.map(tbSuspected, TBSuspectedDTO.class); + dto.setUpdateDate(tbSuspected.getLastModDate()); + dto.setUpdatedBy(tbSuspected.getModifiedBy()); + out.put(tbSuspected.getId(), gson.toJson(dto)); + // Same side effect as getByUserId, so incentives don't depend on which pull the app uses. + if (Boolean.TRUE.equals(tbSuspected.getIsConfirmed())) { incentiveLogicService.incentiveForTbSuspected( tbSuspected.getBenId(), tbSuspected.getVisitDate(), @@ -158,11 +266,7 @@ public String getByUserId(GetBenRequestHandler request) { ); } } - TBSuspectedRequestDTO tbSuspectedRequestDTO = new TBSuspectedRequestDTO(); - tbSuspectedRequestDTO.setTbSuspectedList(dtos); - tbSuspectedRequestDTO.setUserId(request.getAshaId()); - Gson gson = new GsonBuilder().setDateFormat("MMM dd, yyyy h:mm:ss a").create(); - return gson.toJson(tbSuspectedRequestDTO); + return out; } } diff --git a/src/main/java/com/iemr/flw/service/impl/UTPReronaPaymentJob.java b/src/main/java/com/iemr/flw/service/impl/UTPReronaPaymentJob.java index c45f18e1c..3edb88456 100644 --- a/src/main/java/com/iemr/flw/service/impl/UTPReronaPaymentJob.java +++ b/src/main/java/com/iemr/flw/service/impl/UTPReronaPaymentJob.java @@ -11,6 +11,8 @@ import com.iemr.flw.repo.iemr.IncentivesRepo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -23,7 +25,7 @@ @Slf4j @Component -public class UTPReronaPaymentJob { +public class UTPReronaPaymentJob implements CommandLineRunner { @Autowired private IncentiveRecordRepo recordRepo; @@ -34,18 +36,76 @@ public class UTPReronaPaymentJob { @Autowired private UtpreronaPaymentIntegrationImpl paymentService; - // Runs automatically on 1st of every month at midnight - @Scheduled(cron = "0 0 0 1 * *") - public void sendMonthlyPayments() { - triggerPayment(); - } +// // Runs automatically on 1st of every month at midnight +// @Scheduled(cron = "0 0 0 1 * *") +// public void sendMonthlyPayments() { +// triggerPayment(); +// } + + @Override + public void run(String... args) { + log.info("========================================"); + + log.info("FLW application started successfully and is now running."); + + log.info("========================================"); + // triggerStaticPayment(); + } // ✅ Separate method — call this for immediate testing + + public void triggerStaticPayment() { + try { + Period period = new Period(); + period.setStart("2026-01-01"); + period.setEnd("2026-01-31"); + + VerifiedBy verifiedBy = new VerifiedBy(); + verifiedBy.setEmployeeId("34455"); + verifiedBy.setName("ASHA Supervisor/CHO name"); + + List items = new ArrayList<>(); + + PaymentItem item1 = new PaymentItem(); + item1.setActivityCode("1"); + item1.setCount("3"); + item1.setIncentiveAmount("300"); + items.add(item1); + + PaymentItem item2 = new PaymentItem(); + item2.setActivityCode("2"); + item2.setCount("4"); + item2.setIncentiveAmount("400"); + items.add(item2); + + PaymentRequest paymentRequest = new PaymentRequest( + "4d0d8f3a-0bh9b-fff4e7edd-8bdd7a-rfgggdjhCCrrj", + "AMRIT", + period, + "1857708", + "2026-02-01T02:00:00+05:30", + verifiedBy, + items + ); + + log.info("========================================"); + log.info("STATIC PAYMENT REQUEST"); + log.info("Payload: {}", new Gson().toJson(paymentRequest)); + log.info("========================================"); + + paymentService.sendPaymentRequest(paymentRequest); + + log.info("Static payment request sent successfully."); + + } catch (Exception e) { + log.error("Static payment request failed: {}", e.getMessage(), e); + } + } public void triggerPayment() { LocalDate today = LocalDate.now(); -// ✅ Current month ka data — testing ke liye +// ✅ Current month ka data LocalDate firstDay = today.withDayOfMonth(1); // Mar 1, 2026 LocalDate lastDay = today; // Mar 11, 2026 (aaj) @@ -192,4 +252,6 @@ public void triggerPayment() { log.info("Job Complete | Success: {} | Failed: {} | Skipped: {}", success, failed, skipped); log.info("========================================"); } + + } \ No newline at end of file diff --git a/src/main/java/com/iemr/flw/service/impl/UserServiceImpl.java b/src/main/java/com/iemr/flw/service/impl/UserServiceImpl.java index 7537e5917..b3fef7139 100644 --- a/src/main/java/com/iemr/flw/service/impl/UserServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/UserServiceImpl.java @@ -28,7 +28,15 @@ public class UserServiceImpl implements UserService { public UserServiceRoleDTO getUserDetail(Integer userId) { logger.info("calling getUserRole for userId: " + userId); - UserServiceRoleDTO userRole = userServiceRoleRepo.getUserRole(userId).get(0); + List roles = userServiceRoleRepo.getUserRole(userId); + if (roles == null || roles.isEmpty()) { + throw new IllegalStateException("No active role mapping for userId " + userId); + } + // A user can have several mapping rows. Taking whichever came first dropped the villages + // on the others; use the STOP-TB (Nikshay) row when there is one, and the villages of + // every row for that same service. + UserServiceRoleDTO userRole = primaryRole(userId, roles); + mergeVillages(userRole, roles); userRole.setFacilityData(facilityDataService.buildFacilityData(userId, userRole.getRoleName())); // Two different villages can legitimately share the same name (confirmed live: @@ -103,6 +111,36 @@ public UserServiceRoleDTO getUserDetail(Integer userId) { return userRole; } + private UserServiceRoleDTO primaryRole(Integer userId, List roles) { + if (roles.size() == 1) return roles.get(0); + for (UserServiceRoleDTO role : roles) { + List nikshay = userServiceRoleRepo.getNikshayMappingRows(userId, role.getProviderServiceMapId()); + if (nikshay != null && !nikshay.isEmpty()) return role; + } + return roles.get(0); + } + + // Unions the parallel villageId/villageName lists of every row for the chosen row's service, + // keeping first-seen order and one entry per village ID. Rows whose two lists don't line up + // are left out of the merge rather than risk pairing an ID with the wrong name. + private void mergeVillages(UserServiceRoleDTO userRole, List roles) { + java.util.LinkedHashMap byId = new java.util.LinkedHashMap<>(); + for (UserServiceRoleDTO role : roles) { + if (!java.util.Objects.equals(role.getProviderServiceMapId(), userRole.getProviderServiceMapId())) continue; + if (role.getVillageId() == null || role.getVillageId().isBlank() + || role.getVillageName() == null || role.getVillageName().isBlank()) continue; + String[] ids = role.getVillageId().split(",", -1); + String[] names = role.getVillageName().split(",", -1); + if (ids.length != names.length) continue; + for (int i = 0; i < ids.length; i++) { + if (!ids[i].isBlank()) byId.putIfAbsent(ids[i].trim(), names[i].trim()); + } + } + if (byId.isEmpty()) return; + userRole.setVillageId(String.join(",", byId.keySet())); + userRole.setVillageName(String.join(",", byId.values())); + } + // Zips the parallel villageId/villageName comma lists and rejoins each pair as // "name (id)", e.g. "Sanapindapadar,Sanapindapadar" + "275135,275136" becomes // "Sanapindapadar (275135),Sanapindapadar (275136)". Falls back to the original, diff --git a/src/main/java/com/iemr/flw/service/impl/UtpreronaPaymentIntegrationImpl.java b/src/main/java/com/iemr/flw/service/impl/UtpreronaPaymentIntegrationImpl.java index f0e5a5544..ad4e7d381 100644 --- a/src/main/java/com/iemr/flw/service/impl/UtpreronaPaymentIntegrationImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/UtpreronaPaymentIntegrationImpl.java @@ -1,9 +1,8 @@ package com.iemr.flw.service.impl; import com.fasterxml.jackson.databind.ObjectMapper; +import com.iemr.flw.dto.PaymentResponse; import com.iemr.flw.dto.iemr.PaymentRequest; -import io.swagger.v3.oas.models.responses.ApiResponse; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; @@ -15,9 +14,13 @@ @Service public class UtpreronaPaymentIntegrationImpl { -// @Value("${ssdPortalUrl}") - private String API_URL = "https://nhmssd.assam.gov.in/APPMS_2024_25/api/utpreronaPayment.php"; - private String API_KEY = "41202fa384eab2725a17bbac58cf708bf04cd1dd5175010941606156dc36d6b5"; + + private String API_URL = + "https://nhmssd.assam.gov.in/APPMS_2024_25/api/api_receipt_entry.php"; + + private String API_KEY = + "41202fa384eab2725a17bbac58cf708bf04cd1dd5175010941606156dc36d6b5"; + private static final int TIMEOUT_SECONDS = 30; private final HttpClient httpClient; @@ -27,16 +30,21 @@ public UtpreronaPaymentIntegrationImpl() { this.httpClient = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(TIMEOUT_SECONDS)) .build(); + this.objectMapper = new ObjectMapper(); } - public ApiResponse sendPaymentRequest(PaymentRequest paymentRequest) + public PaymentResponse sendPaymentRequest(PaymentRequest paymentRequest) throws IOException, InterruptedException { - String jsonBody = objectMapper.writeValueAsString(paymentRequest); + String jsonBody = + objectMapper.writeValueAsString(paymentRequest); + System.out.println("Sending Request:"); - System.out.println(objectMapper.writerWithDefaultPrettyPrinter() - .writeValueAsString(paymentRequest)); + System.out.println( + objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(paymentRequest) + ); HttpRequest httpRequest = HttpRequest.newBuilder() .uri(URI.create(API_URL)) @@ -47,20 +55,51 @@ public ApiResponse sendPaymentRequest(PaymentRequest paymentRequest) .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); - HttpResponse httpResponse = httpClient.send( - httpRequest, - HttpResponse.BodyHandlers.ofString() + HttpResponse httpResponse = + httpClient.send( + httpRequest, + HttpResponse.BodyHandlers.ofString() + ); + + System.out.println( + "Response Status: " + httpResponse.statusCode() + ); + + System.out.println( + "Response Body: " + httpResponse.body() ); - System.out.println("Response Status: " + httpResponse.statusCode()); - System.out.println("Response Body: " + httpResponse.body()); + if (httpResponse.statusCode() >= 200 + && httpResponse.statusCode() < 300) { + + PaymentResponse response = + objectMapper.readValue( + httpResponse.body(), + PaymentResponse.class + ); + + System.out.println( + "Submission ID: " + response.getSubmissionId() + ); + + System.out.println( + "Status: " + response.getStatus() + ); + + System.out.println( + "Receipt: " + response.getReceipt() + ); + + return response; - if (httpResponse.statusCode() == 200) { - return objectMapper.readValue(httpResponse.body(), ApiResponse.class); } else { - throw new IOException("API Error - Status: " + httpResponse.statusCode() - + " | Body: " + httpResponse.body()); + + throw new IOException( + "API Error - Status: " + + httpResponse.statusCode() + + " | Body: " + + httpResponse.body() + ); } } - -} +} \ No newline at end of file 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 7f1104d1d..52a33bc0a 100644 --- a/src/main/java/com/iemr/flw/service/impl/VillageLevelFormServiceImpl.java +++ b/src/main/java/com/iemr/flw/service/impl/VillageLevelFormServiceImpl.java @@ -149,8 +149,15 @@ private Boolean saveVhncFormData(VhncFormDTO vhncFormDTO, Integer userID) { vhncForm.setFormType("VHNC"); vhncFormRepo.save(vhncForm); + if(vhncFormDTO.getVhncDate()!=null && !vhncFormDTO.getVhncDate().isEmpty()){ - checkVhncIncentive(vhncForm.getCreatedDate(),Math.toIntExact(vhncForm.getUserId())); + Timestamp timestamp = Timestamp.valueOf( + LocalDate.parse(vhncForm.getVhncDate(), DateTimeFormatter.ofPattern("dd-MM-yyyy")) + .atStartOfDay() + ); + + checkVhncIncentive(timestamp,Math.toIntExact(vhncForm.getUserId())); + } return true; } @@ -170,8 +177,17 @@ private Boolean savePhcForm(PhcReviewMeetingFormDTO dto, Integer userID) { phcReviewForm.setImage2(dto.getImage2()); phcReviewForm.setFormType("PHC"); phcReviewFormRepo.save(phcReviewForm); + if(dto.getPhcReviewDate()!=null && !dto.getPhcReviewDate().isEmpty()){ + + Timestamp timestamp = Timestamp.valueOf( + LocalDate.parse(phcReviewForm.getPhcReviewDate(), DateTimeFormatter.ofPattern("dd-MM-yyyy")) + .atStartOfDay() + ); + + checkPhcMeetingIncentive(timestamp,Math.toIntExact(phcReviewForm.getUserId())); + } + - checkPhcMeetingIncentive(phcReviewForm.getCreatedDate(),Math.toIntExact(phcReviewForm.getUserId())); return true; } @@ -217,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( @@ -278,8 +294,18 @@ private Boolean saveVhndFormData(VHNDFormDTO vhndFormDTO, Integer userID) { vhndForm.setFormType("VHND"); vhndRepo.save(vhndForm); + if (vhndFormDTO.getVhndDate() != null + && !vhndFormDTO.getVhndDate().isEmpty()) { - checkVhndIncentive(vhndForm.getCreatedDate(),vhndForm.getUserId()); + Timestamp timestamp = Timestamp.valueOf( + LocalDate.parse( + vhndFormDTO.getVhndDate(), + DateTimeFormatter.ofPattern("dd-MM-yyyy") + ).atStartOfDay() + ); + + checkVhndIncentive(timestamp, vhndForm.getUserId()); + } return true; @@ -339,6 +365,7 @@ record = new IncentiveActivityRecord(); record.setUpdatedBy(userRepo.getUserNamedByUserId(userId)); record.setAshaId(userId); record.setBenId(0L); + record.setIsDefaultActivity(true); record.setAmount(Long.valueOf(activity.getRate())); record.setIsEligible(true); recordRepo.save(record); @@ -375,6 +402,7 @@ record = new IncentiveActivityRecord(); record.setUpdatedBy(userRepo.getUserNamedByUserId(userId)); record.setAshaId(userId); record.setBenId(0L); + record.setIsDefaultActivity(true); record.setAmount(Long.valueOf(activity.getRate())); record.setIsEligible(true); recordRepo.save(record); diff --git a/src/main/java/com/iemr/flw/service/impl/WorklistPagePrefetch.java b/src/main/java/com/iemr/flw/service/impl/WorklistPagePrefetch.java new file mode 100644 index 000000000..b34acf9a8 --- /dev/null +++ b/src/main/java/com/iemr/flw/service/impl/WorklistPagePrefetch.java @@ -0,0 +1,191 @@ +package com.iemr.flw.service.impl; + +import com.iemr.flw.domain.iemr.BenAnthropometryDetail; +import com.iemr.flw.domain.iemr.BenFlowStatus; +import com.iemr.flw.domain.iemr.BenPhysicalVitalDetail; +import com.iemr.flw.domain.identity.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.stereotype.Component; + +import java.math.BigInteger; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Loads everything the worklist builder needs for one page with one query per table, instead of + * ~16 queries per beneficiary. Each query keeps the ordering of the per-row query it replaces, so + * the response is unchanged. + * + * IDs are written into the SQL as numeric literals rather than bound as a collection: Hibernate 6.4.1 + * in this service throws "Binding is multi-valued" for every IN (:list) parameter. The values are + * Long/BigInteger read from the database, so the literal list cannot carry anything but digits. + */ +@Component +public class WorklistPagePrefetch { + + @PersistenceContext(unitName = "db_identity") + private EntityManager identityEm; + + @PersistenceContext(unitName = "db_iemr") + private EntityManager iemrEm; + + public static class Page { + final Map> mappingsByAddress = new HashMap<>(); + final Map details = new HashMap<>(); + final Map accounts = new HashMap<>(); + final Map images = new HashMap<>(); + final Map addresses = new HashMap<>(); + final Map contacts = new HashMap<>(); + final Map> benIdsByRegId = new HashMap<>(); + final Map> rmnchByRegId = new HashMap<>(); + final Map> bornBirthByRegId = new HashMap<>(); + final Map> householdsById = new HashMap<>(); + final Map> flowsByRegId = new HashMap<>(); + final Map> anthropometryByRegId = new HashMap<>(); + final Map> vitalsByRegId = new HashMap<>(); + final Set regIdsWithHealthId = new HashSet<>(); + final Map userIdByName = new HashMap<>(); + + public List mappings(BigInteger addressId) { + return mappingsByAddress.getOrDefault(addressId, Collections.emptyList()); + } + public RMNCHMBeneficiarydetail detail(BigInteger id) { return details.get(id); } + public RMNCHMBeneficiaryAccount account(BigInteger id) { return accounts.get(id); } + public RMNCHMBeneficiaryImage image(BigInteger id) { return images.get(id); } + public RMNCHMBeneficiaryaddress address(BigInteger id) { return addresses.get(id); } + public RMNCHMBeneficiarycontact contact(BigInteger id) { return contacts.get(id); } + public List benIds(Long regId) { return benIdsByRegId.getOrDefault(regId, Collections.emptyList()); } + public List rmnch(Long regId) { return rmnchByRegId.getOrDefault(regId, Collections.emptyList()); } + public List bornBirth(Long regId) { return bornBirthByRegId.getOrDefault(regId, Collections.emptyList()); } + public List households(Long householdId) { return householdsById.getOrDefault(householdId, Collections.emptyList()); } + public List flows(Long regId) { return flowsByRegId.getOrDefault(regId, Collections.emptyList()); } + public List anthropometry(Long regId) { return anthropometryByRegId.getOrDefault(regId, Collections.emptyList()); } + public List vitals(Long regId) { return vitalsByRegId.getOrDefault(regId, Collections.emptyList()); } + public boolean hasHealthId(Long regId) { return regIdsWithHealthId.contains(regId); } + public Map userIdCache() { return userIdByName; } + } + + /** The page's addresses in the order of {@code ids} (the worklist page order); ids with no row are skipped, as before. */ + public List addressesInOrder(List ids) { + Map byId = new HashMap<>(); + putAll(byId, entities(identityEm, RMNCHMBeneficiaryaddress.class, + "SELECT * FROM db_identity.i_beneficiaryaddress WHERE BenAddressID IN (%s)", + new LinkedHashSet<>(ids)), RMNCHMBeneficiaryaddress::getBenAddressID); + return ids.stream().map(byId::get).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public Page load(List pageAddresses) { + Page page = new Page(); + pageAddresses.forEach(a -> page.addresses.put(a.getBenAddressID(), a)); + // The builder looks mappings up by address.getId(), which is the VanSerialNo column, not the + // address primary key - keep that exactly. + Set lookupIds = pageAddresses.stream().map(RMNCHMBeneficiaryaddress::getId) + .filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new)); + if (lookupIds.isEmpty()) return page; + + // getByAddressID: ORDER BY BenMapId DESC + for (RMNCHMBeneficiarymapping m : entities(identityEm, RMNCHMBeneficiarymapping.class, + "SELECT * FROM db_identity.i_beneficiarymapping WHERE BenAddressId IN (%s) ORDER BY BenMapId DESC", lookupIds)) { + page.mappingsByAddress.computeIfAbsent(m.getBenAddressId(), k -> new ArrayList<>()).add(m); + } + // the builder only uses the first (newest) mapping of each address + List used = page.mappingsByAddress.values().stream() + .map(l -> l.get(0)).collect(Collectors.toList()); + + putAll(page.details, entities(identityEm, RMNCHMBeneficiarydetail.class, + "SELECT * FROM db_identity.i_beneficiarydetails WHERE BeneficiaryDetailsId IN (%s)", + ids(used, RMNCHMBeneficiarymapping::getBenDetailsId)), RMNCHMBeneficiarydetail::getBeneficiaryDetailsId); + putAll(page.accounts, entities(identityEm, RMNCHMBeneficiaryAccount.class, + "SELECT * FROM db_identity.i_beneficiaryaccount WHERE BenAccountID IN (%s)", + ids(used, RMNCHMBeneficiarymapping::getBenAccountID)), RMNCHMBeneficiaryAccount::getBenAccountID); + putAll(page.images, entities(identityEm, RMNCHMBeneficiaryImage.class, + "SELECT * FROM db_identity.i_beneficiaryimage WHERE BenImageId IN (%s)", + ids(used, RMNCHMBeneficiarymapping::getBenImageId)), RMNCHMBeneficiaryImage::getBenImageId); + putAll(page.contacts, entities(identityEm, RMNCHMBeneficiarycontact.class, + "SELECT * FROM db_identity.i_beneficiarycontacts WHERE BenContactsID IN (%s)", + ids(used, RMNCHMBeneficiarymapping::getBenContactsId)), RMNCHMBeneficiarycontact::getBenContactsID); + Set missingAddressIds = ids(used, RMNCHMBeneficiarymapping::getBenAddressId); + missingAddressIds.removeAll(page.addresses.keySet()); + putAll(page.addresses, entities(identityEm, RMNCHMBeneficiaryaddress.class, + "SELECT * FROM db_identity.i_beneficiaryaddress WHERE BenAddressID IN (%s)", missingAddressIds), + RMNCHMBeneficiaryaddress::getBenAddressID); + + Set regIds = ids(used, RMNCHMBeneficiarymapping::getBenRegId); + if (regIds.isEmpty()) return page; + + for (Object[] r : rows(identityEm, + "SELECT BenRegId, BeneficiaryID FROM db_identity.m_beneficiaryregidmapping WHERE BenRegId IN (%s)", regIds)) { + page.benIdsByRegId.computeIfAbsent(toLong(r[0]), k -> new ArrayList<>()).add(toBigInteger(r[1])); + } + // getDetailsByRegID: ORDER BY beneficiaryDetails_RmnchId DESC + group(page.rmnchByRegId, entities(identityEm, RMNCHBeneficiaryDetailsRmnch.class, + "SELECT * FROM db_identity.i_beneficiarydetails_rmnch WHERE BeneficiaryRegID IN (%s) ORDER BY beneficiaryDetails_RmnchId DESC", + regIds), RMNCHBeneficiaryDetailsRmnch::getBenRegId); + group(page.bornBirthByRegId, entities(identityEm, RMNCHBornBirthDetails.class, + "SELECT * FROM db_identity.i_bornbirthdeatils WHERE BeneficiaryRegID IN (%s) ORDER BY BornBirthDeatilsId", + regIds), RMNCHBornBirthDetails::getBenRegId); + + // households of the newest rmnch row (the one the builder uses) + Set householdIds = page.rmnchByRegId.values().stream().map(l -> l.get(0).getHouseoldId()) + .filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new)); + group(page.householdsById, entities(identityEm, RMNCHHouseHoldDetails.class, + "SELECT * FROM db_identity.i_householddetails WHERE houseoldId IN (%s) ORDER BY houseHoldDetailsId", + householdIds), RMNCHHouseHoldDetails::getHouseoldId); + + group(page.flowsByRegId, entities(iemrEm, BenFlowStatus.class, + "SELECT * FROM db_iemr.i_ben_flow_outreach WHERE beneficiary_reg_id IN (%s) ORDER BY ben_flow_id", + regIds), BenFlowStatus::getBeneficiaryRegID); + // findByBeneficiaryRegIDOrderByCreatedDateDesc + group(page.anthropometryByRegId, entities(iemrEm, BenAnthropometryDetail.class, + "SELECT * FROM db_iemr.t_phy_anthropometry WHERE BeneficiaryRegID IN (%s) ORDER BY CreatedDate DESC", + regIds), BenAnthropometryDetail::getBeneficiaryRegID); + group(page.vitalsByRegId, entities(iemrEm, BenPhysicalVitalDetail.class, + "SELECT * FROM db_iemr.t_phy_vitals WHERE BeneficiaryRegID IN (%s) ORDER BY CreatedDate DESC", + regIds), BenPhysicalVitalDetail::getBeneficiaryRegID); + + for (Object id : iemrEm.createNativeQuery(String.format( + "SELECT DISTINCT BeneficiaryRegID FROM db_iemr.m_benhealthidmapping WHERE BeneficiaryRegID IN (%s)", + literalList(regIds))).getResultList()) { + page.regIdsWithHealthId.add(toLong(id)); + } + return page; + } + + @SuppressWarnings("unchecked") + private static List entities(EntityManager em, Class type, String sql, Collection ids) { + if (ids.isEmpty()) return Collections.emptyList(); + return em.createNativeQuery(String.format(sql, literalList(ids)), type).getResultList(); + } + + @SuppressWarnings("unchecked") + private static List rows(EntityManager em, String sql, Collection ids) { + if (ids.isEmpty()) return Collections.emptyList(); + return em.createNativeQuery(String.format(sql, literalList(ids))).getResultList(); + } + + private static Set ids(List rows, Function getter) { + return rows.stream().map(getter).filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private static void putAll(Map target, List rows, Function key) { + rows.forEach(r -> target.putIfAbsent(key.apply(r), r)); + } + + private static void group(Map> target, List rows, Function key) { + rows.forEach(r -> target.computeIfAbsent(key.apply(r), k -> new ArrayList<>()).add(r)); + } + + // Only whole numbers ever reach the SQL text: anything else is rejected rather than quoted. + private static String literalList(Collection ids) { + return ids.stream().map(id -> { + if (id instanceof Number) return new BigInteger(id.toString()).toString(); + throw new IllegalArgumentException("Non-numeric id in worklist prefetch: " + id); + }).collect(Collectors.joining(",")); + } + + private static Long toLong(Object o) { return o == null ? null : ((Number) o).longValue(); } + + private static BigInteger toBigInteger(Object o) { return o == null ? null : new BigInteger(o.toString()); } +} diff --git a/src/main/java/com/iemr/flw/service/sync/StopTBSyncType.java b/src/main/java/com/iemr/flw/service/sync/StopTBSyncType.java new file mode 100644 index 000000000..8ac1db509 --- /dev/null +++ b/src/main/java/com/iemr/flw/service/sync/StopTBSyncType.java @@ -0,0 +1,151 @@ +package com.iemr.flw.service.sync; + +import java.util.List; + +/** + * The record types the STOP-TB changes feed (/stoptb/sync/changes) serves, in snapshot order + * (beneficiaries first, so a tablet has the person before their TB records). + * + * For each type: + * - {@code logEntity}: the entity name the hub's change-log triggers write (AMRIT-DB + * dbiemr/V132__Sync_Change_Log.sql and dbidentity/V32__Sync_Change_Log_Beneficiary.sql); + * - {@code snapshotKeySql}: the keys in scope, for the first full download, with the same filters + * the type's getAll endpoint applies. Parameters: :psm, :villages, :after, :limit; + * - {@code sourceTables}: every table the type's record is built from. Each must have the change-log + * triggers, or edits to it would never reach tablets. The feed checks this on the hub + * ({@code StopTBSyncServiceImpl#isSupported}) and a unit test checks every mapped table is either + * here or deliberately not synced. + * + * @author Piramal Swasthya + */ +public enum StopTBSyncType { + + BENEFICIARY("beneficiary", "beneficiary", + "SELECT DISTINCT f.beneficiary_reg_id FROM db_iemr.i_ben_flow_outreach f " + + "WHERE f.providerServiceMapID = :psm AND f.villageID IN (:villages) AND f.deleted = 0 " + + "AND f.beneficiary_reg_id > :after ORDER BY f.beneficiary_reg_id LIMIT :limit", + List.of("db_identity.i_beneficiarymapping", "db_identity.i_beneficiarydetails", + "db_identity.i_beneficiarydetails_rmnch", "db_identity.i_bornbirthdeatils", + "db_identity.i_beneficiaryaddress", "db_identity.i_beneficiarycontacts", + "db_identity.i_beneficiaryimage", "db_identity.i_beneficiaryaccount", + "db_identity.i_householddetails", "db_iemr.t_phy_anthropometry", "db_iemr.t_phy_vitals", + "db_iemr.i_ben_flow_outreach")), + + TB_SCREENING("tbScreening", "tb_screening", + "SELECT s.id FROM db_iemr.tb_screening s WHERE (s.deleted = 0 OR s.deleted IS NULL) " + + "AND s.ben_reg_id IN (" + Scope.REG + ") AND s.id > :after ORDER BY s.id LIMIT :limit", + List.of("db_iemr.tb_screening")), + + GENERAL_EXAMINATION("generalExamination", "general_examination", + "SELECT e.id FROM db_iemr.tb_stoptb_general_examination e WHERE e.deleted = 0 " + + "AND e.beneficiary_reg_id IN (" + Scope.REG + ") AND e.id > :after ORDER BY e.id LIMIT :limit", + List.of("db_iemr.tb_stoptb_general_examination")), + + /** OPD visits saved to the standard tables, keyed by BenVisitID. */ + GENERAL_OPD("generalOpd", "opd_visit", + "SELECT v.BenVisitID FROM db_iemr.t_benvisitdetail v WHERE v.Deleted = 0 AND v.ProviderServiceMapID = :psm " + + "AND v.BeneficiaryRegID IN (" + Scope.REG + ") AND v.BenVisitID > :after ORDER BY v.BenVisitID LIMIT :limit", + // t_prescription holds the visit's note (latest prescription instruction) + List.of("db_iemr.t_benvisitdetail", "db_iemr.t_benchiefcomplaint", "db_iemr.t_prescribeddrug", + "db_iemr.t_prescription")), + + /** Older free-text OPD rows; same record shape as GENERAL_OPD. */ + GENERAL_OPD_LEGACY("generalOpdLegacy", "general_opd", + "SELECT o.id FROM db_iemr.tb_stoptb_general_opd o WHERE o.deleted = 0 " + + "AND o.ben_reg_id IN (" + Scope.REG + ") AND o.id > :after ORDER BY o.id LIMIT :limit", + List.of("db_iemr.tb_stoptb_general_opd")), + + DIAGNOSTICS("diagnostics", "diagnostics", + "SELECT d.id FROM db_iemr.tb_stoptb_diagnostics d WHERE d.deleted = 0 " + + "AND d.ben_reg_id IN (" + Scope.REG + ") AND d.id > :after ORDER BY d.id LIMIT :limit", + List.of("db_iemr.tb_stoptb_diagnostics")), + + TB_SUSPECTED("tbSuspected", "tb_suspected", + "SELECT t.id FROM db_iemr.tb_suspected t WHERE t.ben_id IN (" + Scope.BEN_ID + ") " + + "AND t.id > :after ORDER BY t.id LIMIT :limit", + List.of("db_iemr.tb_suspected")), + + TB_CONFIRMED("tbConfirmed", "tb_confirmed", + "SELECT c.id FROM db_iemr.tb_confirmed_cases c WHERE c.ben_id IN (" + Scope.BEN_ID + ") " + + "AND c.id > :after ORDER BY c.id LIMIT :limit", + List.of("db_iemr.tb_confirmed_cases")), + + FORM_RESPONSE("formResponse", "form_response", + "SELECT r.responseId FROM db_iemr.t_form_response r WHERE r.beneficiaryId IN (" + Scope.BEN_ID_NOT_DELETED + ") " + + "AND r.responseId > :after ORDER BY r.responseId LIMIT :limit", + List.of("db_iemr.t_form_response", "db_iemr.t_section_response", "db_iemr.t_question_response")), + + DIAGNOSTIC_ORDER("diagnosticOrder", "diagnostic_order", + "SELECT o.id FROM db_iemr.tb_diagnostic_order o WHERE o.deleted = 0 " + + "AND o.beneficiary_id IN (" + Scope.BEN_ID_NOT_DELETED + ") AND o.id > :after ORDER BY o.id LIMIT :limit", + List.of("db_iemr.tb_diagnostic_order", "db_iemr.tb_diagnostic_result")); + + /** Beneficiaries in the requested villages, the same way today's getAll endpoints scope. */ + static final class Scope { + static final String REG = "SELECT f.beneficiary_reg_id FROM db_iemr.i_ben_flow_outreach f " + + "WHERE f.providerServiceMapID = :psm AND f.villageID IN (:villages)"; + static final String BEN_ID = "SELECT f.beneficiary_id FROM db_iemr.i_ben_flow_outreach f " + + "WHERE f.providerServiceMapID = :psm AND f.villageID IN (:villages)"; + static final String BEN_ID_NOT_DELETED = BEN_ID + " AND f.deleted = 0"; + // The same, for specific beneficiaries (by BenRegId) that just came into scope. + static final String REG_FOR_BENS = "SELECT f.beneficiary_reg_id FROM db_iemr.i_ben_flow_outreach f " + + "WHERE f.beneficiary_reg_id IN (:regIds)"; + static final String BEN_ID_FOR_BENS = "SELECT f.beneficiary_id FROM db_iemr.i_ben_flow_outreach f " + + "WHERE f.beneficiary_reg_id IN (:regIds)"; + + private Scope() { + } + } + + private final String apiName; + private final String logEntity; + private final String snapshotKeySql; + private final String keysForBeneficiariesSql; + private final List sourceTables; + + StopTBSyncType(String apiName, String logEntity, String snapshotKeySql, List sourceTables) { + this.apiName = apiName; + this.logEntity = logEntity; + this.snapshotKeySql = snapshotKeySql; + // The snapshot query with its village scope swapped for "these beneficiaries" and its keyset + // paging dropped. Parameters: :regIds (and :psm where the snapshot filters on it). + this.keysForBeneficiariesSql = snapshotKeySql + .replace(Scope.BEN_ID_NOT_DELETED, Scope.BEN_ID_FOR_BENS) + .replace(Scope.BEN_ID, Scope.BEN_ID_FOR_BENS) + .replace(Scope.REG, Scope.REG_FOR_BENS) + .replaceAll(" AND \\S+ > :after ORDER BY \\S+ LIMIT :limit$", ""); + this.sourceTables = sourceTables; + } + + /** The "type" field of an item in the feed. */ + public String apiName() { + return apiName; + } + + public String logEntity() { + return logEntity; + } + + public String snapshotKeySql() { + return snapshotKeySql; + } + + /** + * Keys of this type's records for beneficiaries that just came into a tablet's scope, so their + * older records (logged before they were in scope) still reach it. Not used for BENEFICIARY. + */ + public String keysForBeneficiariesSql() { + return keysForBeneficiariesSql; + } + + public List sourceTables() { + return sourceTables; + } + + public static StopTBSyncType fromLogEntity(String entity) { + for (StopTBSyncType t : values()) { + if (t.logEntity.equals(entity)) return t; + } + return null; + } +} diff --git a/src/main/java/com/iemr/flw/service/sync/SyncToken.java b/src/main/java/com/iemr/flw/service/sync/SyncToken.java new file mode 100644 index 000000000..0055b0247 --- /dev/null +++ b/src/main/java/com/iemr/flw/service/sync/SyncToken.java @@ -0,0 +1,117 @@ +package com.iemr.flw.service.sync; + +/** + * The tablet's opaque sync position. Two kinds: + * + * d.<serverId>.<seq>.<issuedEpochSeconds>.<typesMask>.<scopeHash> + * changes after change-log line seq on this hub; + * s.<serverId>.<snapshotSeq>.<typeIndex>.<afterKey>.<typesMask>.<scopeHash> + * part-way through a first download: the next page starts at type typeIndex, after afterKey, + * and when it finishes the tablet continues with changes after snapshotSeq. + * + * serverId is the hub's MySQL server_uuid, so a token from another hub is never trusted. The issue + * time lets the hub tell when the log lines a token points after may have been cleaned up. + * typesMask is the set of record types the token was issued for (bit = StopTBSyncType ordinal): a + * token only covers those types, so a request for a different set starts a new snapshot. scopeHash + * is the service and village list the token was issued for (UserSyncScope.scopeHash); a different + * scope also starts a new snapshot. + * + * @author Piramal Swasthya + */ +public final class SyncToken { + + public enum Kind { DELTA, SNAPSHOT } + + private final Kind kind; + private final String serverId; + private final long seq; + private final long issuedAt; + private final int typeIndex; + private final long afterKey; + private final long typesMask; + private final long scopeHash; + + private SyncToken(Kind kind, String serverId, long seq, long issuedAt, int typeIndex, long afterKey, + long typesMask, long scopeHash) { + this.kind = kind; + this.serverId = serverId; + this.seq = seq; + this.issuedAt = issuedAt; + this.typeIndex = typeIndex; + this.afterKey = afterKey; + this.typesMask = typesMask; + this.scopeHash = scopeHash; + } + + public static SyncToken delta(String serverId, long seq, long issuedAt, long typesMask, long scopeHash) { + return new SyncToken(Kind.DELTA, serverId, seq, issuedAt, 0, 0, typesMask, scopeHash); + } + + public static SyncToken snapshot(String serverId, long snapshotSeq, int typeIndex, long afterKey, long typesMask, + long scopeHash) { + return new SyncToken(Kind.SNAPSHOT, serverId, snapshotSeq, 0, typeIndex, afterKey, typesMask, scopeHash); + } + + /** null for a missing or unreadable token; the caller then starts a snapshot. */ + public static SyncToken parse(String token) { + if (token == null || token.isBlank()) return null; + String[] p = token.trim().split("\\."); + try { + if (p.length == 6 && "d".equals(p[0]) && !p[1].isEmpty()) { + return delta(p[1], Long.parseLong(p[2]), Long.parseLong(p[3]), Long.parseLong(p[4]), + Long.parseLong(p[5])); + } + if (p.length == 7 && "s".equals(p[0]) && !p[1].isEmpty()) { + return snapshot(p[1], Long.parseLong(p[2]), Integer.parseInt(p[3]), Long.parseLong(p[4]), + Long.parseLong(p[5]), Long.parseLong(p[6])); + } + } catch (NumberFormatException e) { + return null; + } + return null; + } + + public String encode() { + return kind == Kind.DELTA + ? "d." + serverId + "." + seq + "." + issuedAt + "." + typesMask + "." + scopeHash + : "s." + serverId + "." + seq + "." + typeIndex + "." + afterKey + "." + typesMask + "." + scopeHash; + } + + public Kind kind() { + return kind; + } + + public String serverId() { + return serverId; + } + + /** DELTA: last change-log line applied. SNAPSHOT: the line changes continue from afterwards. */ + public long seq() { + return seq; + } + + public long issuedAt() { + return issuedAt; + } + + public int typeIndex() { + return typeIndex; + } + + public long afterKey() { + return afterKey; + } + + public long typesMask() { + return typesMask; + } + + public long scopeHash() { + return scopeHash; + } + + @Override + public String toString() { + return encode(); + } +} diff --git a/src/main/java/com/iemr/flw/service/sync/UserSyncScope.java b/src/main/java/com/iemr/flw/service/sync/UserSyncScope.java new file mode 100644 index 000000000..0573daf04 --- /dev/null +++ b/src/main/java/com/iemr/flw/service/sync/UserSyncScope.java @@ -0,0 +1,66 @@ +package com.iemr.flw.service.sync; + +import java.util.*; + +/** + * The villages a user may sync, per provider service map: every active role-mapping row counts + * (a user can have several), each holding a comma-separated village list. + * + * @author Piramal Swasthya + */ +public final class UserSyncScope { + + private final Map> villagesByService; + + private UserSyncScope(Map> villagesByService) { + this.villagesByService = villagesByService; + } + + /** rows: [ProviderServiceMapID, Villageid csv] from m_userservicerolemapping. */ + public static UserSyncScope fromMappingRows(List rows) { + Map> out = new HashMap<>(); + for (Object[] row : rows) { + if (row == null || row[0] == null) continue; + Set villages = out.computeIfAbsent(((Number) row[0]).intValue(), k -> new TreeSet<>()); + if (row.length > 1 && row[1] != null) { + for (String id : row[1].toString().split(",")) { + try { + villages.add(Integer.valueOf(id.trim())); + } catch (NumberFormatException ignored) { + // malformed entry: not a village the user can sync + } + } + } + } + return new UserSyncScope(out); + } + + /** + * The villages to sync for this request: all of the user's villages for the service when none + * are asked for, otherwise exactly the ones asked for. Throws when the service or any village + * isn't mapped to the user. + */ + public List resolve(Integer providerServiceMapID, List requested) { + Set allowed = villagesByService.get(providerServiceMapID); + if (allowed == null) { + throw new IllegalArgumentException("User is not mapped to providerServiceMapID " + providerServiceMapID); + } + if (requested == null || requested.isEmpty()) { + if (allowed.isEmpty()) throw new IllegalArgumentException("User has no villages for this service"); + return new ArrayList<>(allowed); + } + List outside = new ArrayList<>(); + for (Integer v : requested) if (!allowed.contains(v)) outside.add(v); + if (!outside.isEmpty()) { + throw new IllegalArgumentException("Villages not assigned to the user: " + outside); + } + return new ArrayList<>(new TreeSet<>(requested)); + } + + /** A stable fingerprint of a service and its village list, bound into sync tokens. */ + public static long scopeHash(Integer providerServiceMapID, List villages) { + List sorted = new ArrayList<>(villages); + Collections.sort(sorted); + return Integer.toUnsignedLong((providerServiceMapID + "|" + sorted).hashCode()); + } +} diff --git a/src/main/java/com/iemr/flw/utils/ApiTimingFilter.java b/src/main/java/com/iemr/flw/utils/ApiTimingFilter.java new file mode 100644 index 000000000..d93159634 --- /dev/null +++ b/src/main/java/com/iemr/flw/utils/ApiTimingFilter.java @@ -0,0 +1,40 @@ +package com.iemr.flw.utils; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +// One [API-TIMING] line per request (method, path, status, total ms incl. auth filters) so camp-laptop +// logs show every API's time: journalctl -u amrit-flw-api | grep API-TIMING +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class ApiTimingFilter implements Filter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + long start = System.currentTimeMillis(); + try { + chain.doFilter(servletRequest, servletResponse); + } finally { + logger.info("[API-TIMING] {} {} status={} ms={}", request.getMethod(), request.getRequestURI(), + response.getStatus(), System.currentTimeMillis() - start); + } + } +} diff --git a/src/main/java/com/iemr/flw/utils/response/OutputResponse.java b/src/main/java/com/iemr/flw/utils/response/OutputResponse.java index d4cb36705..7b5f7f8e3 100644 --- a/src/main/java/com/iemr/flw/utils/response/OutputResponse.java +++ b/src/main/java/com/iemr/flw/utils/response/OutputResponse.java @@ -83,6 +83,15 @@ public void setResponse(String message) { } + // Same result as setResponse(gson.toJson(x)) for an object/array, without writing the JSON text + // and parsing it back: pass gson.toJsonTree(x). + public void setResponse(JsonElement tree) { + this.data = tree; + statusCode = SUCCESS; + errorMessage = "Success"; + status = "Success"; + } + public void setError(Throwable thrown) { Date currDate = Calendar.getInstance().getTime(); logger.info("error happened due to " + thrown.getClass().getSimpleName() + " at " + currDate.toString()); diff --git a/src/main/resources/application-swagger.properties b/src/main/resources/application-swagger.properties new file mode 100644 index 000000000..d8fdf13dd --- /dev/null +++ b/src/main/resources/application-swagger.properties @@ -0,0 +1,79 @@ +# Swagger server URLs +api.dev.url=${API_DEV_URL:https://amritwprdev.piramalswasthya.org} +api.uat.url=${API_UAT_URL:https://uatamrit.piramalswasthya.org} +api.demo.url=${API_DEMO_URL:https://amritdemo.piramalswasthya.org} + +# --- Swagger Profile Configuration --- +# Disable H2 console to avoid dataSource bean creation issues +spring.h2.console.enabled=false + +# Primary Database (using H2 for swagger generation) +spring.datasource.url=jdbc:h2:mem:swaggerdb +spring.datasource.username=SA +spring.datasource.password= +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=none +spring.jpa.show-sql=false +spring.jpa.properties.hibernate.format_sql=false + +# Secondary Database (using H2 for swagger generation) +secondary.datasource.url=jdbc:h2:mem:swaggerdb2 +secondary.datasource.username=SA +secondary.datasource.password= +secondary.datasource.driver-class-name=org.h2.Driver + +# Redis Configuration (dummy for swagger) +spring.redis.host=localhost +spring.redis.port=6379 +spring.redis.password= + +# Basic configuration +server.port=8080 +logging.level.root=INFO +logging.level.org.springframework=INFO +management.endpoints.web.exposure.include=health,info +management.server.address=127.0.0.1 +management.server.port=8081 + +# JWT Configuration +jwt.secret=sample-jwt-secret + +# CORS Configuration +cors.allowed-origins=http://localhost:8080,http://localhost:9000 + +# External API placeholders (dummy values for swagger generation) +fhir-url=http://localhost:8093/ +tm-url=http://localhost:8089/ + +# Naming strategies +spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl +spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + +# Jackson configuration +spring.jackson.serialization.fail-on-empty-beans=false +spring.jackson.default-property-inclusion=NON_NULL +spring.jackson.deserialization.fail-on-unknown-properties=false + +# Session configuration (none for swagger profile - Redis not available in CI) +spring.session.store-type=none +iemr.extend.expiry.time=true +iemr.session.expiry.time=7200 +enableIPValidation=false + +# Logging +logging.path=logs/ +logging.file.name=logs/flw-api.log + +# SpringDoc configuration for OpenAPI/Swagger +springdoc.api-docs.path=/v3/api-docs +springdoc.swagger-ui.path=/swagger-ui.html +springdoc.swagger-ui.enabled=true +springdoc.api-docs.enabled=true +springdoc.swagger-ui.display-request-duration=true +springdoc.show-actuator=false + +# Additional CommonAPI placeholders +door-to-door-page-size=10 +get-HRP-Status=ANC/getHRPStatus +getHealthID=healthID/getBenhealthID diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 84cca4815..e03faaa00 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -35,7 +35,7 @@ spring.datasource.tomcat.remove-abandoned=true spring.datasource.tomcat.logAbandoned=true spring.datasource.continue-on-error=true spring.datasource.tomcat.remove-abandoned-timeout=600 -door-to-door-page-size=10 +door-to-door-page-size=50 #Get-HRP-Status get-HRP-Status=ANC/getHRPStatus @@ -62,7 +62,7 @@ spring.jpa.defer-datasource-initialization=true fhir-url= # TM Config -tm-url=https://amritdemo.piramalswasthya.org/tm-api +tm-url= springdoc.api-docs.enabled=true springdoc.swagger-ui.enabled=true @@ -71,4 +71,64 @@ spring.main.allow-bean-definition-overriding=true spring.redis.password= spring.redis.port=6379 -notificationurl=https://uatamrit.piramalswasthya.org/common-api/firebaseNotification/sendNotification +notificationurl=firebaseNotification/sendNotification + + + +server.port=8081 +# FHIR Config + +# TM Config +##--------------------------------------------## Primary db------------------------------------------------------------------- + +spring.datasource.url=jdbc:mysql://localhost:3306/db_iemr +spring.datasource.username=root +spring.datasource.password=1234 +spring.datasource.driver-class-name=com.mysql.jdbc.Driver + +##--------------------------------------------## Secondary db------------------------------------------------------------------- + +secondary.datasource.url=jdbc:mysql://localhost:3306/db_identity +secondary.datasource.username=root +secondary.datasource.password=1234 +secondary.datasource.driver-class-name=com.mysql.jdbc.Driver + + + +logging.path=logs/ +logging.file.name=logs/flw-api.log + +jwt.secret=my-32-character-ultra-secure-and-ultra-long-secret + +# Redis Config + +cors.allowed-origins=http://localhost:* + +source-address= +sms-username= +sms-password= +send-message-url= + +crash.logs.base.path= + +# ABHA Token Config +abha.client.id= +abha.client.secret= +abha.token.url= +abha.xcmid= + + +#crash.logs.base.path=@env.CRASH_LOGS_PATH@ + +# ABHA Token Config + + +# GovThealth API Config +govthealth.user.details.url=@env.GOVTHEALTH_USER_DETAILS_URL@ +govthealth.user.id=@env.GOVTHEALTH_USER_ID@ +govthealth.password=@env.GOVTHEALTH_PASSWORD@ +common-api-base-url= + + + + diff --git a/src/test/java/com/iemr/flw/FlwApplicationTest.java b/src/test/java/com/iemr/flw/FlwApplicationTest.java deleted file mode 100644 index 7ce759d2c..000000000 --- a/src/test/java/com/iemr/flw/FlwApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.iemr.flw; - - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -public class FlwApplicationTest { - - @Test - public void contextLoads() { - } -} \ No newline at end of file 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..4e61d0b52 --- /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/controller/BeneficiaryControllerTest.java b/src/test/java/com/iemr/flw/controller/BeneficiaryControllerTest.java new file mode 100644 index 000000000..056791158 --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/BeneficiaryControllerTest.java @@ -0,0 +1,75 @@ +package com.iemr.flw.controller; + +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.service.BeneficiaryService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class BeneficiaryControllerTest { + private MockMvc mockMvc; + + @Mock + private BeneficiaryService beneficiaryService; + + @InjectMocks + private BeneficiaryController controller; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void getBeneficiaryDataByAsha_success() throws Exception { + GetBenRequestHandler req = new GetBenRequestHandler(); + when(beneficiaryService.getBenData(any(), anyString())).thenReturn("data"); + mockMvc.perform(post("/beneficiary/getBeneficiaryData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void getBeneficiaryDataByAsha_noRecordFound() throws Exception { + GetBenRequestHandler req = new GetBenRequestHandler(); + when(beneficiaryService.getBenData(any(), anyString())).thenReturn(null); + mockMvc.perform(post("/beneficiary/getBeneficiaryData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void getBeneficiaryDataByAsha_invalidRequest() throws Exception { + mockMvc.perform(post("/beneficiary/getBeneficiaryData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getBeneficiaryDataByAsha_exception() throws Exception { + when(beneficiaryService.getBenData(any(), anyString())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/beneficiary/getBeneficiaryData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/iemr/flw/controller/CbacControllerTest.java b/src/test/java/com/iemr/flw/controller/CbacControllerTest.java new file mode 100644 index 000000000..ed24fd5c7 --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/CbacControllerTest.java @@ -0,0 +1,121 @@ +package com.iemr.flw.controller; + +import com.google.gson.Gson; +import com.iemr.flw.dto.identity.CbacDTO; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.service.CbacService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +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.Collections; +import java.util.List; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class CbacControllerTest { + private MockMvc mockMvc; + + @Mock + private CbacService cbacService; + + @InjectMocks + private CbacController controller; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void getAllCbacDetailsByUserId_success() throws Exception { + when(cbacService.getByUserId(any())).thenReturn("data"); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/cbac/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllCbacDetailsByUserId_noRecordFound() throws Exception { + when(cbacService.getByUserId(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/cbac/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllCbacDetailsByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/cbac/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getAllCbacDetailsByUserId_exception() throws Exception { + when(cbacService.getByUserId(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/cbac/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveAllCbacDetailsByUserId_success() throws Exception { + List dtos = Collections.singletonList(new CbacDTO()); + when(cbacService.save(any())).thenReturn("data"); + mockMvc.perform(post("/cbac/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveAllCbacDetailsByUserId_noRecordFound() throws Exception { + List dtos = Collections.singletonList(new CbacDTO()); + when(cbacService.save(any())).thenReturn(null); + mockMvc.perform(post("/cbac/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveAllCbacDetailsByUserId_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/cbac/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveAllCbacDetailsByUserId_exception() throws Exception { + List dtos = Collections.singletonList(new CbacDTO()); + when(cbacService.save(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/cbac/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/iemr/flw/controller/ChildCareControllerTest.java b/src/test/java/com/iemr/flw/controller/ChildCareControllerTest.java new file mode 100644 index 000000000..f8c679474 --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/ChildCareControllerTest.java @@ -0,0 +1,339 @@ +//package com.iemr.flw.controller; +// +//import com.google.gson.Gson; +//import com.iemr.flw.dto.identity.GetBenRequestHandler; +//import com.iemr.flw.dto.iemr.*; +//import com.iemr.flw.service.ChildCareService; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.mockito.InjectMocks; +//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.Collections; +//import java.util.List; +//import static org.mockito.ArgumentMatchers.any; +//import static org.mockito.Mockito.when; +//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +// +//class ChildCareControllerTest { +// private MockMvc mockMvc; +// +// @Mock +// private ChildCareService childCareService; +// +// @InjectMocks +// private ChildCareController controller; +// +// @BeforeEach +// void setUp() { +// MockitoAnnotations.openMocks(this); +// mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); +// } +// +// @Test +// void saveHbycRecords_success() throws Exception { +// List dtos = Collections.singletonList(new HbycDTO()); +// when(childCareService.registerHBYC(any())).thenReturn("data"); +// mockMvc.perform(post("/child-care/hbyc/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void saveHbycRecords_noRecordFound() throws Exception { +// List dtos = Collections.singletonList(new HbycDTO()); +// when(childCareService.registerHBYC(any())).thenReturn(null); +// mockMvc.perform(post("/child-care/hbyc/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()) +// .andExpect(result -> { +// String content = result.getResponse().getContentAsString(); +// assert content.contains("No record found"); +// }); +// } +// +// @Test +// void saveHbycRecords_invalidRequest() throws Exception { +// mockMvc.perform(post("/child-care/hbyc/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content("null")) +// .andExpect(status().isBadRequest()); +// } +// +// @Test +// void saveHbycRecords_exception() throws Exception { +// List dtos = Collections.singletonList(new HbycDTO()); +// when(childCareService.registerHBYC(any())).thenThrow(new RuntimeException("fail")); +// mockMvc.perform(post("/child-care/hbyc/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getHbycRecords_success() throws Exception { +// when(childCareService.getHbycRecords(any())).thenReturn(Collections.singletonList(new HbycDTO())); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/hbyc/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getHbycRecords_noRecordFound() throws Exception { +// when(childCareService.getHbycRecords(any())).thenReturn(null); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/hbyc/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getHbycRecords_invalidRequest() throws Exception { +// mockMvc.perform(post("/child-care/hbyc/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content("null")) +// .andExpect(status().isBadRequest()); +// } +// +// @Test +// void getHbycRecords_exception() throws Exception { +// when(childCareService.getHbycRecords(any())).thenThrow(new RuntimeException("fail")); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/hbyc/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void saveHBNCVisit_success() throws Exception { +// List dtos = Collections.singletonList(new HbncRequestDTO()); +// when(childCareService.saveHBNCDetails(any())).thenReturn("data"); +// mockMvc.perform(post("/child-care/hbncVisit/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void saveHBNCVisit_noRecordFound() throws Exception { +// List dtos = Collections.singletonList(new HbncRequestDTO()); +// when(childCareService.saveHBNCDetails(any())).thenReturn(null); +// mockMvc.perform(post("/child-care/hbncVisit/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()) +// .andExpect(result -> { +// String content = result.getResponse().getContentAsString(); +// assert content.contains("Saving hbnc data to db failed"); +// }); +// } +// +// @Test +// void saveHBNCVisit_invalidRequest() throws Exception { +// mockMvc.perform(post("/child-care/hbncVisit/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content("[]")) +// .andExpect(status().isOk()) +// .andExpect(result -> { +// String content = result.getResponse().getContentAsString(); +// assert content.contains("Invalid/NULL request obj"); +// }); +// } +// +// @Test +// void saveHBNCVisit_exception() throws Exception { +// List dtos = Collections.singletonList(new HbncRequestDTO()); +// when(childCareService.saveHBNCDetails(any())).thenThrow(new RuntimeException("fail")); +// mockMvc.perform(post("/child-care/hbncVisit/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getHBNCVisitDetails_success() throws Exception { +// when(childCareService.getHBNCDetails(any())).thenReturn(Collections.singletonList(new HbncRequestDTO())); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/hbncVisit/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getHBNCVisitDetails_noRecordFound() throws Exception { +// when(childCareService.getHBNCDetails(any())).thenReturn(null); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/hbncVisit/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getHBNCVisitDetails_invalidRequest() throws Exception { +// mockMvc.perform(post("/child-care/hbncVisit/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content("null")) +// .andExpect(status().isBadRequest()); +// } +// +// @Test +// void getHBNCVisitDetails_exception() throws Exception { +// when(childCareService.getHBNCDetails(any())).thenThrow(new RuntimeException("fail")); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/hbncVisit/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void saveChildVaccinationDetails_success() throws Exception { +// List dtos = Collections.singletonList(new ChildVaccinationDTO()); +// when(childCareService.saveChildVaccinationDetails(any())).thenReturn("data"); +// mockMvc.perform(post("/child-care/vaccination/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void saveChildVaccinationDetails_noRecordFound() throws Exception { +// List dtos = Collections.singletonList(new ChildVaccinationDTO()); +// when(childCareService.saveChildVaccinationDetails(any())).thenReturn(null); +// mockMvc.perform(post("/child-care/vaccination/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()) +// .andExpect(result -> { +// String content = result.getResponse().getContentAsString(); +// assert content.contains("Saving Child vaccination data to db failed"); +// }); +// } +// +// @Test +// void saveChildVaccinationDetails_invalidRequest() throws Exception { +// mockMvc.perform(post("/child-care/vaccination/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content("[]")) +// .andExpect(status().isOk()) +// .andExpect(result -> { +// String content = result.getResponse().getContentAsString(); +// assert content.contains("Invalid/NULL request obj"); +// }); +// } +// +// @Test +// void saveChildVaccinationDetails_exception() throws Exception { +// List dtos = Collections.singletonList(new ChildVaccinationDTO()); +// when(childCareService.saveChildVaccinationDetails(any())).thenThrow(new RuntimeException("fail")); +// mockMvc.perform(post("/child-care/vaccination/saveAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(dtos))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getChildVaccinationDetails_success() throws Exception { +// when(childCareService.getChildVaccinationDetails(any())).thenReturn(Collections.singletonList(new ChildVaccinationDTO())); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/vaccination/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getChildVaccinationDetails_noRecordFound() throws Exception { +// when(childCareService.getChildVaccinationDetails(any())).thenReturn(null); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/vaccination/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getChildVaccinationDetails_invalidRequest() throws Exception { +// mockMvc.perform(post("/child-care/vaccination/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content("null")) +// .andExpect(status().isBadRequest()); +// } +// +// @Test +// void getChildVaccinationDetails_exception() throws Exception { +// when(childCareService.getChildVaccinationDetails(any())).thenThrow(new RuntimeException("fail")); +// GetBenRequestHandler req = new GetBenRequestHandler(); +// mockMvc.perform(post("/child-care/vaccination/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .content(new Gson().toJson(req))) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getAllChildVaccines_success() throws Exception { +// when(childCareService.getAllChildVaccines(any())).thenReturn(Collections.singletonList(new VaccineDTO())); +// mockMvc.perform(get("/child-care/vaccine/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .param("category", "cat1")) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getAllChildVaccines_noRecordFound() throws Exception { +// when(childCareService.getAllChildVaccines(any())).thenReturn(null); +// mockMvc.perform(get("/child-care/vaccine/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .param("category", "cat1")) +// .andExpect(status().isOk()); +// } +// +// @Test +// void getAllChildVaccines_exception() throws Exception { +// when(childCareService.getAllChildVaccines(any())).thenThrow(new RuntimeException("fail")); +// mockMvc.perform(get("/child-care/vaccine/getAll") +// .contentType(MediaType.APPLICATION_JSON) +// .header("Authorization", "Bearer token") +// .param("category", "cat1")) +// .andExpect(status().isOk()); +// } +//} diff --git a/src/test/java/com/iemr/flw/controller/CoupleControllerTest.java b/src/test/java/com/iemr/flw/controller/CoupleControllerTest.java index b4cf29e84..61895098e 100644 --- a/src/test/java/com/iemr/flw/controller/CoupleControllerTest.java +++ b/src/test/java/com/iemr/flw/controller/CoupleControllerTest.java @@ -1,233 +1,205 @@ package com.iemr.flw.controller; -import static org.mockito.Mockito.*; - +import com.google.gson.Gson; import com.iemr.flw.dto.identity.GetBenRequestHandler; import com.iemr.flw.dto.iemr.EligibleCoupleDTO; import com.iemr.flw.dto.iemr.EligibleCoupleTrackingDTO; import com.iemr.flw.service.CoupleService; -import com.iemr.flw.utils.ApiResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -import java.util.Arrays; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import java.util.Collections; import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; class CoupleControllerTest { - - @InjectMocks - private CoupleController coupleController; + private MockMvc mockMvc; @Mock private CoupleService coupleService; + @InjectMocks + private CoupleController controller; + @BeforeEach void setUp() { - MockitoAnnotations.initMocks(this); - } - -// @Test -// void testSaveEligibleCouple() { -// // Arrange -// List eligibleCoupleDTOs = Arrays.asList(new EligibleCoupleDTO()); -// -// when(coupleService.registerEligibleCouple(any())).thenReturn("Save successful"); -// -// // Act -// ResponseEntity responseEntity = coupleController.saveEligibleCouple(eligibleCoupleDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals("Save successful", apiResponse.getData()); -// -// verify(coupleService, times(1)).registerEligibleCouple(eligibleCoupleDTOs); -// } -// -// @Test -// void testSaveEligibleCouple_Exception() { -// // Arrange -// List eligibleCoupleDTOs = Arrays.asList(new EligibleCoupleDTO()); -// -// when(coupleService.registerEligibleCouple(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = coupleController.saveEligibleCouple(eligibleCoupleDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(coupleService, times(1)).registerEligibleCouple(eligibleCoupleDTOs); -// } -// -// @Test -// void testGetEligibleCouple() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(198); -// -// List responseDTOs = Arrays.asList(new EligibleCoupleDTO()); -// when(coupleService.getEligibleCoupleRegRecords(any())).thenReturn(responseDTOs); -// -// // Act -// ResponseEntity responseEntity = coupleController.getEligibleCouple(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals(responseDTOs, apiResponse.getData()); -// -// verify(coupleService, times(1)).getEligibleCoupleRegRecords(requestDTO); -// } -// -// @Test -// void testGetEligibleCouple_Exception() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1098); -// -// when(coupleService.getEligibleCoupleRegRecords(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = coupleController.getEligibleCouple(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(coupleService, times(1)).getEligibleCoupleRegRecords(requestDTO); -// } -// -// @Test -// void testSaveEligibleCoupleTracking() { -// // Arrange -// List eligibleCoupleTrackingDTOS = Arrays.asList(new EligibleCoupleTrackingDTO()); -// -// when(coupleService.registerEligibleCoupleTracking(any())).thenReturn("Save successful"); -// -// // Act -// ResponseEntity responseEntity = coupleController.saveEligibleCoupleTracking(eligibleCoupleTrackingDTOS, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals("Save successful", apiResponse.getData()); -// -// verify(coupleService, times(1)).registerEligibleCoupleTracking(eligibleCoupleTrackingDTOS); -// } -// -// @Test -// void testSaveEligibleCoupleTracking_Exception() { -// // Arrange -// List eligibleCoupleTrackingDTOS = Arrays.asList(new EligibleCoupleTrackingDTO()); -// -// when(coupleService.registerEligibleCoupleTracking(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = coupleController.saveEligibleCoupleTracking(eligibleCoupleTrackingDTOS, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(coupleService, times(1)).registerEligibleCoupleTracking(eligibleCoupleTrackingDTOS); -// } -// -// @Test -// void testGetEligibleCoupleTracking() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1298); -// -// List responseDTOs = Arrays.asList(new EligibleCoupleTrackingDTO()); -// when(coupleService.getEligibleCoupleTracking(any())).thenReturn(responseDTOs); -// -// // Act -// ResponseEntity responseEntity = coupleController.getEligibleCoupleTracking(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals(responseDTOs, apiResponse.getData()); -// -// verify(coupleService, times(1)).getEligibleCoupleTracking(requestDTO); -// } -// -// @Test -// void testGetEligibleCoupleTracking_Exception() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1028); -// -// when(coupleService.getEligibleCoupleTracking(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = coupleController.getEligibleCoupleTracking(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(coupleService, times(1)).getEligibleCoupleTracking(requestDTO); -// } + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void saveEligibleCouple_success() throws Exception { + List dtos = Collections.singletonList(new EligibleCoupleDTO()); + when(coupleService.registerEligibleCouple(any())).thenReturn("data"); + mockMvc.perform(post("/couple/register/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveEligibleCouple_noRecordFound() throws Exception { + List dtos = Collections.singletonList(new EligibleCoupleDTO()); + when(coupleService.registerEligibleCouple(any())).thenReturn(null); + mockMvc.perform(post("/couple/register/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveEligibleCouple_invalidRequest() throws Exception { + mockMvc.perform(post("/couple/register/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveEligibleCouple_exception() throws Exception { + List dtos = Collections.singletonList(new EligibleCoupleDTO()); + when(coupleService.registerEligibleCouple(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/couple/register/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveEligibleCoupleTracking_success() throws Exception { + List dtos = Collections.singletonList(new EligibleCoupleTrackingDTO()); + when(coupleService.registerEligibleCoupleTracking(any())).thenReturn("data"); + mockMvc.perform(post("/couple/tracking/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveEligibleCoupleTracking_noRecordFound() throws Exception { + List dtos = Collections.singletonList(new EligibleCoupleTrackingDTO()); + when(coupleService.registerEligibleCoupleTracking(any())).thenReturn(null); + mockMvc.perform(post("/couple/tracking/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveEligibleCoupleTracking_invalidRequest() throws Exception { + mockMvc.perform(post("/couple/tracking/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveEligibleCoupleTracking_exception() throws Exception { + List dtos = Collections.singletonList(new EligibleCoupleTrackingDTO()); + when(coupleService.registerEligibleCoupleTracking(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/couple/tracking/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getEligibleCouple_success() throws Exception { + when(coupleService.getEligibleCoupleRegRecords(any())).thenReturn("data"); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/couple/register/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getEligibleCouple_noRecordFound() throws Exception { + when(coupleService.getEligibleCoupleRegRecords(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/couple/register/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getEligibleCouple_invalidRequest() throws Exception { + mockMvc.perform(post("/couple/register/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getEligibleCouple_exception() throws Exception { + when(coupleService.getEligibleCoupleRegRecords(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/couple/register/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getEligibleCoupleTracking_success() throws Exception { + when(coupleService.getEligibleCoupleTracking(any())).thenReturn(Collections.singletonList(new EligibleCoupleTrackingDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/couple/tracking/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getEligibleCoupleTracking_noRecordFound() throws Exception { + when(coupleService.getEligibleCoupleTracking(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/couple/tracking/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getEligibleCoupleTracking_invalidRequest() throws Exception { + mockMvc.perform(post("/couple/tracking/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getEligibleCoupleTracking_exception() throws Exception { + when(coupleService.getEligibleCoupleTracking(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/couple/tracking/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } } diff --git a/src/test/java/com/iemr/flw/controller/DeathReportsControllerTest.java b/src/test/java/com/iemr/flw/controller/DeathReportsControllerTest.java new file mode 100644 index 000000000..f41b6a5e5 --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/DeathReportsControllerTest.java @@ -0,0 +1,214 @@ +package com.iemr.flw.controller; + +import com.google.gson.Gson; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.CdrDTO; +import com.iemr.flw.dto.iemr.MdsrDTO; +import com.iemr.flw.service.DeathReportsService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +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.Collections; +import java.util.List; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class DeathReportsControllerTest { + private MockMvc mockMvc; + + @Mock + private DeathReportsService deathReportsService; + + @InjectMocks + private DeathReportsController controller; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void saveCdrRecords_success() throws Exception { + List dtos = Collections.singletonList(new CdrDTO()); + when(deathReportsService.registerCDR(any())).thenReturn("data"); + mockMvc.perform(post("/death-reports/cdr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveCdrRecords_noRecordFound() throws Exception { + List dtos = Collections.singletonList(new CdrDTO()); + when(deathReportsService.registerCDR(any())).thenReturn(null); + mockMvc.perform(post("/death-reports/cdr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + + @Test + void saveCdrRecords_invalidRequest() throws Exception { + mockMvc.perform(post("/death-reports/cdr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveCdrRecords_exception() throws Exception { + List dtos = Collections.singletonList(new CdrDTO()); + when(deathReportsService.registerCDR(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/death-reports/cdr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveMdsrRecords_success() throws Exception { + List dtos = Collections.singletonList(new MdsrDTO()); + when(deathReportsService.registerMDSR(any())).thenReturn("data"); + mockMvc.perform(post("/death-reports/mdsr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveMdsrRecords_noRecordFound() throws Exception { + List dtos = Collections.singletonList(new MdsrDTO()); + when(deathReportsService.registerMDSR(any())).thenReturn(null); + mockMvc.perform(post("/death-reports/mdsr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + + @Test + void saveMdsrRecords_invalidRequest() throws Exception { + mockMvc.perform(post("/death-reports/mdsr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveMdsrRecords_exception() throws Exception { + List dtos = Collections.singletonList(new MdsrDTO()); + when(deathReportsService.registerMDSR(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/death-reports/mdsr/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getCdrRecords_success() throws Exception { + when(deathReportsService.getCdrRecords(any())).thenReturn(Collections.singletonList(new CdrDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/death-reports/cdr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getCdrRecords_noRecordFound() throws Exception { + when(deathReportsService.getCdrRecords(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/death-reports/cdr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + + } + + @Test + void getCdrRecords_invalidRequest() throws Exception { + mockMvc.perform(post("/death-reports/cdr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getCdrRecords_exception() throws Exception { + when(deathReportsService.getCdrRecords(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/death-reports/cdr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getMdsrRecords_success() throws Exception { + when(deathReportsService.getMdsrRecords(any())).thenReturn(Collections.singletonList(new MdsrDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/death-reports/mdsr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getMdsrRecords_noRecordFound() throws Exception { + when(deathReportsService.getMdsrRecords(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/death-reports/mdsr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getMdsrRecords_invalidRequest() throws Exception { + mockMvc.perform(post("/death-reports/mdsr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getMdsrRecords_exception() throws Exception { + when(deathReportsService.getMdsrRecords(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/death-reports/mdsr/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/iemr/flw/controller/HighRiskControllerTest.java b/src/test/java/com/iemr/flw/controller/HighRiskControllerTest.java new file mode 100644 index 000000000..1576495bb --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/HighRiskControllerTest.java @@ -0,0 +1,439 @@ +package com.iemr.flw.controller; + +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.*; +import com.iemr.flw.service.HighRiskNonPregnantService; +import com.iemr.flw.service.HighRiskPregnantService; +import com.iemr.flw.service.HighRiskService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class HighRiskControllerTest { + private MockMvc mockMvc; + + @Mock + private HighRiskNonPregnantService highRiskNonPregnantService; + @Mock + private HighRiskPregnantService highRiskPregnantService; + @Mock + private HighRiskService highRiskService; + + @InjectMocks + private HighRiskController controller; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + // Pregnant Assess + @Test + void getAllPregnantAssessByUserId_success() throws Exception { + when(highRiskPregnantService.getAllAssessments(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/pregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void getAllPregnantAssessByUserId_noRecordFound() throws Exception { + when(highRiskPregnantService.getAllAssessments(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/pregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void getAllPregnantAssessByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/pregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void getAllPregnantAssessByUserId_exception() throws Exception { + when(highRiskPregnantService.getAllAssessments(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/pregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllPregnantAssessByUserId_success() throws Exception { + when(highRiskPregnantService.saveAllAssessment(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/pregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllPregnantAssessByUserId_noRecordFound() throws Exception { + when(highRiskPregnantService.saveAllAssessment(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/pregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void saveAllPregnantAssessByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/pregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void saveAllPregnantAssessByUserId_exception() throws Exception { + when(highRiskPregnantService.saveAllAssessment(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/pregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + // Assess + @Test + void getAllAssessByUserId_success() throws Exception { + when(highRiskService.getAllAssessments(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void getAllAssessByUserId_noRecordFound() throws Exception { + when(highRiskService.getAllAssessments(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void getAllAssessByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void getAllAssessByUserId_exception() throws Exception { + when(highRiskService.getAllAssessments(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllAssessByUserId_success() throws Exception { + when(highRiskService.saveAllAssessment(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllAssessByUserId_noRecordFound() throws Exception { + when(highRiskService.saveAllAssessment(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void saveAllAssessByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void saveAllAssessByUserId_exception() throws Exception { + when(highRiskService.saveAllAssessment(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + // Pregnant Track + @Test + void getAllPregnantTrackByUserId_success() throws Exception { + when(highRiskPregnantService.getAllTracking(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/pregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void getAllPregnantTrackByUserId_noRecordFound() throws Exception { + when(highRiskPregnantService.getAllTracking(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/pregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void getAllPregnantTrackByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/pregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void getAllPregnantTrackByUserId_exception() throws Exception { + when(highRiskPregnantService.getAllTracking(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/pregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllPregnantTrackByUserId_success() throws Exception { + when(highRiskPregnantService.saveAllTracking(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/pregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllPregnantTrackByUserId_noRecordFound() throws Exception { + when(highRiskPregnantService.saveAllTracking(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/pregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void saveAllPregnantTrackByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/pregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void saveAllPregnantTrackByUserId_exception() throws Exception { + when(highRiskPregnantService.saveAllTracking(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/pregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + // NonPregnant Assess + @Test + void getAllNonPregnantAssessByUserId_success() throws Exception { + when(highRiskNonPregnantService.getAllAssessment(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/nonPregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void getAllNonPregnantAssessByUserId_noRecordFound() throws Exception { + when(highRiskNonPregnantService.getAllAssessment(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/nonPregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void getAllNonPregnantAssessByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/nonPregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void getAllNonPregnantAssessByUserId_exception() throws Exception { + when(highRiskNonPregnantService.getAllAssessment(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/nonPregnant/assess/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllNonPregnantAssessByUserId_success() throws Exception { + when(highRiskNonPregnantService.saveAllAssessment(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/nonPregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllNonPregnantAssessByUserId_noRecordFound() throws Exception { + when(highRiskNonPregnantService.saveAllAssessment(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/nonPregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void saveAllNonPregnantAssessByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/nonPregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void saveAllNonPregnantAssessByUserId_exception() throws Exception { + when(highRiskNonPregnantService.saveAllAssessment(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/nonPregnant/assess/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + // NonPregnant Track + @Test + void getAllNonPregnantTrackByUserId_success() throws Exception { + when(highRiskNonPregnantService.getAllTracking(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/nonPregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void getAllNonPregnantTrackByUserId_noRecordFound() throws Exception { + when(highRiskNonPregnantService.getAllTracking(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/nonPregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void getAllNonPregnantTrackByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/nonPregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void getAllNonPregnantTrackByUserId_exception() throws Exception { + when(highRiskNonPregnantService.getAllTracking(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/nonPregnant/track/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllNonPregnantTrackByUserId_success() throws Exception { + when(highRiskNonPregnantService.saveAllTracking(any())).thenReturn("data"); + mockMvc.perform(post("/highRisk/nonPregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + @Test + void saveAllNonPregnantTrackByUserId_noRecordFound() throws Exception { + when(highRiskNonPregnantService.saveAllTracking(any())).thenReturn(null); + mockMvc.perform(post("/highRisk/nonPregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + @Test + void saveAllNonPregnantTrackByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/highRisk/nonPregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + @Test + void saveAllNonPregnantTrackByUserId_exception() throws Exception { + when(highRiskNonPregnantService.saveAllTracking(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/highRisk/nonPregnant/track/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/iemr/flw/controller/IncentiveControllerTest.java b/src/test/java/com/iemr/flw/controller/IncentiveControllerTest.java new file mode 100644 index 000000000..fffcb9445 --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/IncentiveControllerTest.java @@ -0,0 +1,163 @@ +package com.iemr.flw.controller; + +import com.google.gson.Gson; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.IncentiveActivityDTO; +import com.iemr.flw.dto.iemr.IncentiveRequestDTO; +import com.iemr.flw.service.IncentiveService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +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.Collections; +import java.util.List; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class IncentiveControllerTest { + private MockMvc mockMvc; + + @Mock + private IncentiveService incentiveService; + + @InjectMocks + private IncentiveController controller; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void saveIncentiveMasterData_success() throws Exception { + List dtos = Collections.singletonList(new IncentiveActivityDTO()); + when(incentiveService.saveIncentivesMaster(any())).thenReturn("data"); + mockMvc.perform(post("/incentive/masterData/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveIncentiveMasterData_noRecordFound() throws Exception { + List dtos = Collections.singletonList(new IncentiveActivityDTO()); + when(incentiveService.saveIncentivesMaster(any())).thenReturn(null); + mockMvc.perform(post("/incentive/masterData/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveIncentiveMasterData_invalidRequest() throws Exception { + mockMvc.perform(post("/incentive/masterData/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveIncentiveMasterData_exception() throws Exception { + List dtos = Collections.singletonList(new IncentiveActivityDTO()); + when(incentiveService.saveIncentivesMaster(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/incentive/masterData/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveIncentiveMasterData_getAll_success() throws Exception { + IncentiveRequestDTO req = new IncentiveRequestDTO(); + when(incentiveService.getIncentiveMaster(any())).thenReturn("data"); + mockMvc.perform(post("/incentive/masterData/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveIncentiveMasterData_getAll_noRecordFound() throws Exception { + IncentiveRequestDTO req = new IncentiveRequestDTO(); + when(incentiveService.getIncentiveMaster(any())).thenReturn(null); + mockMvc.perform(post("/incentive/masterData/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveIncentiveMasterData_getAll_invalidRequest() throws Exception { + mockMvc.perform(post("/incentive/masterData/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveIncentiveMasterData_getAll_exception() throws Exception { + when(incentiveService.getIncentiveMaster(any())).thenThrow(new RuntimeException("fail")); + IncentiveRequestDTO req = new IncentiveRequestDTO(); + mockMvc.perform(post("/incentive/masterData/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllIncentivesByUserId_success() throws Exception { + GetBenRequestHandler req = new GetBenRequestHandler(); + when(incentiveService.getAllIncentivesByUserId(any())).thenReturn("data"); + mockMvc.perform(post("/incentive/fetchUserData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllIncentivesByUserId_noRecordFound() throws Exception { + GetBenRequestHandler req = new GetBenRequestHandler(); + when(incentiveService.getAllIncentivesByUserId(any())).thenReturn(null); + mockMvc.perform(post("/incentive/fetchUserData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllIncentivesByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/incentive/fetchUserData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getAllIncentivesByUserId_exception() throws Exception { + when(incentiveService.getAllIncentivesByUserId(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/incentive/fetchUserData") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java b/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java index e1a682926..444c8d0ec 100644 --- a/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java +++ b/src/test/java/com/iemr/flw/controller/MaternalHealthControllerTest.java @@ -1,426 +1,563 @@ package com.iemr.flw.controller; +import com.google.gson.Gson; +import com.iemr.flw.controller.MaternalHealthController; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.*; +import com.iemr.flw.service.ChildService; import com.iemr.flw.service.DeliveryOutcomeService; import com.iemr.flw.service.InfantService; import com.iemr.flw.service.MaternalHealthService; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; 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.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; class MaternalHealthControllerTest { - - @InjectMocks - private MaternalHealthController maternalHealthController; + private MockMvc mockMvc; @Mock private MaternalHealthService maternalHealthService; - @Mock private DeliveryOutcomeService deliveryOutcomeService; - @Mock private InfantService infantService; + @Mock + private ChildService childService; + + @InjectMocks + private MaternalHealthController controller; @BeforeEach void setUp() { - MockitoAnnotations.initMocks(this); - } - -// @Test -// void testSavePregnantWomanRegistrations() { -// // Arrange -// List pregnantWomanDTOs = Arrays.asList(new PregnantWomanDTO()); -// -// when(pregnantWomanService.registerPregnantWoman(any())).thenReturn("Save successful"); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.savePregnantWomanRegistrations(pregnantWomanDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals("Save successful", apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).registerPregnantWoman(pregnantWomanDTOs); -// } -// -// @Test -// void testSavePregnantWomanRegistrations_Exception() { -// // Arrange -// List pregnantWomanDTOs = Arrays.asList(new PregnantWomanDTO()); -// -// when(pregnantWomanService.registerPregnantWoman(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.savePregnantWomanRegistrations(pregnantWomanDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).registerPregnantWoman(pregnantWomanDTOs); -// } -// -// @Test -// void testGetPregnantWomanList() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1090); -// -// List responseDTOs = Arrays.asList(new PregnantWomanDTO()); -// when(pregnantWomanService.getPregnantWoman(any())).thenReturn(responseDTOs); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getPregnantWomanList(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals(responseDTOs, apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).getPregnantWoman(requestDTO); -// } -// -// @Test -// void testGetPregnantWomanList_Exception() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1090); -// -// when(pregnantWomanService.getPregnantWoman(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getPregnantWomanList(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).getPregnantWoman(requestDTO); -// } -// -// @Test -// void testSaveANCVisit() { -// // Arrange -// List ancVisitDTOs = Arrays.asList(new ANCVisitDTO()); -// -// when(pregnantWomanService.saveANCVisit(any())).thenReturn("Save successful"); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.saveANCVisit(ancVisitDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals("Save successful", apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).saveANCVisit(ancVisitDTOs); -// } -// -// @Test -// void testSaveANCVisit_Exception() { -// // Arrange -// List ancVisitDTOs = Arrays.asList(new ANCVisitDTO()); -// -// when(pregnantWomanService.saveANCVisit(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.saveANCVisit(ancVisitDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).saveANCVisit(ancVisitDTOs); -// } -// -// @Test -// void testGetANCVisitDetails() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1098); -// -// List responseDTOs = Arrays.asList(new ANCVisitDTO()); -// when(pregnantWomanService.getANCVisits(any())).thenReturn(responseDTOs); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getANCVisitDetails(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals(responseDTOs, apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).getANCVisits(requestDTO); -// } -// -// @Test -// void testGetANCVisitDetails_Exception() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1098); -// -// when(pregnantWomanService.getANCVisits(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getANCVisitDetails(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(pregnantWomanService, times(1)).getANCVisits(requestDTO); -// } -// -// @Test -// void testSaveDeliveryOutcome() { -// // Arrange -// List deliveryOutcomeDTOs = Arrays.asList(new DeliveryOutcomeDTO()); -// -// when(deliveryOutcomeService.registerDeliveryOutcome(any())).thenReturn("Save successful"); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.saveDeliveryOutcome(deliveryOutcomeDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals("Save successful", apiResponse.getData()); -// -// verify(deliveryOutcomeService, times(1)).registerDeliveryOutcome(deliveryOutcomeDTOs); -// } -// -// @Test -// void testSaveDeliveryOutcome_Exception() { -// // Arrange -// List deliveryOutcomeDTOs = Arrays.asList(new DeliveryOutcomeDTO()); -// -// when(deliveryOutcomeService.registerDeliveryOutcome(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.saveDeliveryOutcome(deliveryOutcomeDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(deliveryOutcomeService, times(1)).registerDeliveryOutcome(deliveryOutcomeDTOs); -// } -// -// @Test -// void testGetDeliveryOutcome() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1098); -// -// List responseDTOs = Arrays.asList(new DeliveryOutcomeDTO()); -// when(deliveryOutcomeService.getDeliveryOutcome(any())).thenReturn(responseDTOs); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getDeliveryOutcome(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals(responseDTOs, apiResponse.getData()); -// -// verify(deliveryOutcomeService, times(1)).getDeliveryOutcome(requestDTO); -// } -// -// @Test -// void testGetDeliveryOutcome_Exception() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1098); -// -// when(deliveryOutcomeService.getDeliveryOutcome(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getDeliveryOutcome(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(deliveryOutcomeService, times(1)).getDeliveryOutcome(requestDTO); -// } -// -// @Test -// void testSaveInfantList() { -// // Arrange -// List infantRegisterDTOs = Arrays.asList(new InfantRegisterDTO()); -// -// when(infantService.registerInfant(any())).thenReturn("Save successful"); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.saveInfantList(infantRegisterDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals("Save successful", apiResponse.getData()); -// -// verify(infantService, times(1)).registerInfant(infantRegisterDTOs); -// } -// -// @Test -// void testSaveInfantList_Exception() { -// // Arrange -// List infantRegisterDTOs = Arrays.asList(new InfantRegisterDTO()); -// -// when(infantService.registerInfant(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.saveInfantList(infantRegisterDTOs, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(infantService, times(1)).registerInfant(infantRegisterDTOs); -// } -// -// @Test -// void testGetInfantList() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1098); -// -// List responseDTOs = Arrays.asList(new InfantRegisterDTO()); -// when(infantService.getInfantDetails(any())).thenReturn(responseDTOs); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getInfantList(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertTrue(apiResponse.getSuccess()); -// assertNull(apiResponse.getMessage()); -// -// assertEquals(responseDTOs, apiResponse.getData()); -// -// verify(infantService, times(1)).getInfantDetails(requestDTO); -// } -// -// @Test -// void testGetInfantList_Exception() { -// // Arrange -// GetBenRequestHandler requestDTO = new GetBenRequestHandler(); -// requestDTO.setAshaId(1098); -// -// when(infantService.getInfantDetails(any())).thenThrow(new RuntimeException("Test exception")); -// -// // Act -// ResponseEntity responseEntity = maternalHealthController.getInfantList(requestDTO, "AuthorizationToken"); -// -// // Assert -// assertNotNull(responseEntity); -// assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); -// -// ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); -// assertNotNull(apiResponse); -// assertFalse(apiResponse.getSuccess()); -// assertNotNull(apiResponse.getMessage()); -// -// assertNull(apiResponse.getData()); -// -// verify(infantService, times(1)).getInfantDetails(requestDTO); -// } + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void savePregnantWomanRegistrations_success() throws Exception { + List dtos = Collections.singletonList(new PregnantWomanDTO()); + when(maternalHealthService.registerPregnantWoman(any())).thenReturn("success"); + mockMvc.perform(post("/maternalCare/pregnantWoman/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void savePregnantWomanRegistrations_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/maternalCare/pregnantWoman/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getPregnantWomanList_success() throws Exception { + when(maternalHealthService.getPregnantWoman(any())).thenReturn(Collections.singletonList(new PregnantWomanDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pregnantWoman/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + @Test + void getPregnantWomanList_invalidRequest() throws Exception { + mockMvc.perform(post("/maternalCare/pregnantWoman/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getPregnantWomanList_noRecordFound() throws Exception { + when(maternalHealthService.getPregnantWoman(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pregnantWoman/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveANCVisit_success() throws Exception { + List dtos = Collections.singletonList(new ANCVisitDTO()); +// when(maternalHealthService.saveANCVisit(any())).thenReturn("success"); + mockMvc.perform(post("/maternalCare/ancVisit/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveANCVisit_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/maternalCare/ancVisit/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getANCVisitDetails_success() throws Exception { + when(maternalHealthService.getANCVisits(any())).thenReturn(Collections.singletonList(new ANCVisitDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/ancVisit/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getANCVisitDetails_invalidRequest() throws Exception { + mockMvc.perform(post("/maternalCare/ancVisit/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getANCVisitDetails_noRecordFound() throws Exception { + when(maternalHealthService.getANCVisits(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/ancVisit/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveDeliveryOutcome_success() throws Exception { + List dtos = Collections.singletonList(new DeliveryOutcomeDTO()); + when(deliveryOutcomeService.registerDeliveryOutcome(any())).thenReturn("success"); + mockMvc.perform(post("/maternalCare/deliveryOutcome/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveDeliveryOutcome_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/maternalCare/deliveryOutcome/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getDeliveryOutcome_success() throws Exception { + when(deliveryOutcomeService.getDeliveryOutcome(any())).thenReturn(Collections.singletonList(new DeliveryOutcomeDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/deliveryOutcome/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getDeliveryOutcome_invalidRequest() throws Exception { + mockMvc.perform(post("/maternalCare/deliveryOutcome/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getDeliveryOutcome_noRecordFound() throws Exception { + when(deliveryOutcomeService.getDeliveryOutcome(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/deliveryOutcome/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveInfantList_success() throws Exception { + List dtos = Collections.singletonList(new InfantRegisterDTO()); + when(infantService.registerInfant(any())).thenReturn("success"); + mockMvc.perform(post("/maternalCare/infant/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveInfantList_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/maternalCare/infant/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getInfantList_success() throws Exception { + when(infantService.getInfantDetails(any())).thenReturn(Collections.singletonList(new InfantRegisterDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/infant/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getInfantList_invalidRequest() throws Exception { + mockMvc.perform(post("/maternalCare/infant/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getInfantList_noRecordFound() throws Exception { + when(infantService.getInfantDetails(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/infant/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllChildRegisterDetails_success() throws Exception { + when(childService.getByUserId(any())).thenReturn("child"); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/child/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllChildRegisterDetails_invalidRequest() throws Exception { + mockMvc.perform(post("/maternalCare/child/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getAllChildRegisterDetails_noRecordFound() throws Exception { + when(childService.getByUserId(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/child/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveAllChildDetails_success() throws Exception { + List dtos = Collections.singletonList(new ChildRegisterDTO()); + when(childService.save(any())).thenReturn("success"); + mockMvc.perform(post("/maternalCare/child/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveAllChildDetails_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/maternalCare/child/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getAllPmsmaDetails_success() throws Exception { + when(maternalHealthService.getPmsmaRecords(any())).thenReturn(Collections.singletonList(new PmsmaDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pmsma/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllPmsmaDetails_invalidRequest() throws Exception { + mockMvc.perform(post("/maternalCare/pmsma/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getAllPmsmaDetails_noRecordFound() throws Exception { + when(maternalHealthService.getPmsmaRecords(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pmsma/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveAllPmsmaRecords_success() throws Exception { + List dtos = Collections.singletonList(new PmsmaDTO()); + when(maternalHealthService.savePmsmaRecords(any())).thenReturn("success"); + mockMvc.perform(post("/maternalCare/pmsma/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void saveAllPmsmaRecords_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/maternalCare/pmsma/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void savePNCVisit_success() throws Exception { + List dtos = Collections.singletonList(new PNCVisitDTO()); + when(maternalHealthService.savePNCVisit(any())).thenReturn("success"); + mockMvc.perform(post("/maternalCare/pnc/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void savePNCVisit_invalidRequest() throws Exception { + List dtos = Collections.emptyList(); + mockMvc.perform(post("/maternalCare/pnc/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getPNCVisitDetails_success() throws Exception { + when(maternalHealthService.getPNCVisits(any())).thenReturn(Collections.singletonList(new PNCVisitDTO())); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pnc/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getPNCVisitDetails_invalidRequest() throws Exception { + mockMvc.perform(post("/maternalCare/pnc/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getPNCVisitDetails_noRecordFound() throws Exception { + when(maternalHealthService.getPNCVisits(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pnc/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void savePregnantWomanRegistrations_exception() throws Exception { + List dtos = Collections.singletonList(new PregnantWomanDTO()); + when(maternalHealthService.registerPregnantWoman(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/maternalCare/pregnantWoman/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getPregnantWomanList_exception() throws Exception { + when(maternalHealthService.getPregnantWoman(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pregnantWoman/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveANCVisit_exception() throws Exception { + List dtos = Collections.singletonList(new ANCVisitDTO()); +// when(maternalHealthService.saveANCVisit(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/maternalCare/ancVisit/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getANCVisitDetails_exception() throws Exception { + when(maternalHealthService.getANCVisits(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/ancVisit/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveDeliveryOutcome_exception() throws Exception { + List dtos = Collections.singletonList(new DeliveryOutcomeDTO()); + when(deliveryOutcomeService.registerDeliveryOutcome(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/maternalCare/deliveryOutcome/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getDeliveryOutcome_exception() throws Exception { + when(deliveryOutcomeService.getDeliveryOutcome(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/deliveryOutcome/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveInfantList_exception() throws Exception { + List dtos = Collections.singletonList(new InfantRegisterDTO()); + when(infantService.registerInfant(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/maternalCare/infant/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getInfantList_exception() throws Exception { + when(infantService.getInfantDetails(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/infant/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void getAllChildRegisterDetails_exception() throws Exception { + when(childService.getByUserId(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/child/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveAllChildDetails_exception() throws Exception { + List dtos = Collections.singletonList(new ChildRegisterDTO()); + when(childService.save(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/maternalCare/child/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getAllPmsmaDetails_exception() throws Exception { + when(maternalHealthService.getPmsmaRecords(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pmsma/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } + + @Test + void saveAllPmsmaRecords_exception() throws Exception { + List dtos = Collections.singletonList(new PmsmaDTO()); + when(maternalHealthService.savePmsmaRecords(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/maternalCare/pmsma/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void savePNCVisit_exception() throws Exception { + List dtos = Collections.singletonList(new PNCVisitDTO()); + when(maternalHealthService.savePNCVisit(any())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(post("/maternalCare/pnc/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(dtos))) + .andExpect(status().isOk()); + } + + @Test + void getPNCVisitDetails_exception() throws Exception { + when(maternalHealthService.getPNCVisits(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/maternalCare/pnc/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content(new Gson().toJson(req))) + .andExpect(status().isOk()); + } } diff --git a/src/test/java/com/iemr/flw/controller/TBControllerTest.java b/src/test/java/com/iemr/flw/controller/TBControllerTest.java new file mode 100644 index 000000000..212cdbeb7 --- /dev/null +++ b/src/test/java/com/iemr/flw/controller/TBControllerTest.java @@ -0,0 +1,222 @@ +package com.iemr.flw.controller; + +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.TBScreeningRequestDTO; +import com.iemr.flw.dto.iemr.TBSuspectedRequestDTO; +import com.iemr.flw.service.TBScreeningService; +import com.iemr.flw.service.TBSuspectedService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +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.Collections; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class TBControllerTest { + private MockMvc mockMvc; + + @Mock + private TBScreeningService tbScreeningService; + @Mock + private TBSuspectedService tbSuspectedService; + + @InjectMocks + private TBController controller; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void getAllScreeningByUserId_success() throws Exception { + when(tbScreeningService.getByUserId(any())).thenReturn("data"); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/tb/screening/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void getAllScreeningByUserId_noRecordFound() throws Exception { + when(tbScreeningService.getByUserId(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/tb/screening/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + + @Test + void getAllScreeningByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/tb/screening/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getAllScreeningByUserId_exception() throws Exception { + when(tbScreeningService.getByUserId(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/tb/screening/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void saveAllScreeningByUserId_success() throws Exception { + when(tbScreeningService.save(any())).thenReturn("data"); + TBScreeningRequestDTO req = new TBScreeningRequestDTO(); + mockMvc.perform(post("/tb/screening/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void saveAllScreeningByUserId_noRecordFound() throws Exception { + when(tbScreeningService.save(any())).thenReturn(null); + TBScreeningRequestDTO req = new TBScreeningRequestDTO(); + mockMvc.perform(post("/tb/screening/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + + @Test + void saveAllScreeningByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/tb/screening/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveAllScreeningByUserId_exception() throws Exception { + when(tbScreeningService.save(any())).thenThrow(new RuntimeException("fail")); + TBScreeningRequestDTO req = new TBScreeningRequestDTO(); + mockMvc.perform(post("/tb/screening/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void getAllSuspectedByUserId_success() throws Exception { + when(tbSuspectedService.getByUserId(any())).thenReturn("data"); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/tb/suspected/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void getAllSuspectedByUserId_noRecordFound() throws Exception { + when(tbSuspectedService.getByUserId(any())).thenReturn(null); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/tb/suspected/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + + @Test + void getAllSuspectedByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/tb/suspected/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void getAllSuspectedByUserId_exception() throws Exception { + when(tbSuspectedService.getByUserId(any())).thenThrow(new RuntimeException("fail")); + GetBenRequestHandler req = new GetBenRequestHandler(); + mockMvc.perform(post("/tb/suspected/getAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void saveAllSuspectedByUserId_success() throws Exception { + when(tbSuspectedService.save(any())).thenReturn("data"); + TBSuspectedRequestDTO req = new TBSuspectedRequestDTO(); + mockMvc.perform(post("/tb/suspected/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } + + @Test + void saveAllSuspectedByUserId_noRecordFound() throws Exception { + when(tbSuspectedService.save(any())).thenReturn(null); + TBSuspectedRequestDTO req = new TBSuspectedRequestDTO(); + mockMvc.perform(post("/tb/suspected/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()) + .andExpect(result -> { + String content = result.getResponse().getContentAsString(); + assert content.contains("No record found"); + }); + } + + @Test + void saveAllSuspectedByUserId_invalidRequest() throws Exception { + mockMvc.perform(post("/tb/suspected/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("null")) + .andExpect(status().isBadRequest()); + } + + @Test + void saveAllSuspectedByUserId_exception() throws Exception { + when(tbSuspectedService.save(any())).thenThrow(new RuntimeException("fail")); + TBSuspectedRequestDTO req = new TBSuspectedRequestDTO(); + mockMvc.perform(post("/tb/suspected/saveAll") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer token") + .content("{}")) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/iemr/flw/controller/UserControllerTest.java b/src/test/java/com/iemr/flw/controller/UserControllerTest.java index c280ca12f..af339918f 100644 --- a/src/test/java/com/iemr/flw/controller/UserControllerTest.java +++ b/src/test/java/com/iemr/flw/controller/UserControllerTest.java @@ -8,72 +8,50 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; class UserControllerTest { + private MockMvc mockMvc; @Mock private UserService userService; @InjectMocks - private UserController userController; + private UserController controller; @BeforeEach void setUp() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } @Test - void testGetUserDetail_Success() { - // Arrange - Integer userId = 123; - UserServiceRoleDTO userServiceRoleDTO = new UserServiceRoleDTO(); - userServiceRoleDTO.setUserId(userId); - - when(userService.getUserDetail(anyInt())).thenReturn(userServiceRoleDTO); - - // Act - ResponseEntity responseEntity = userController.getUserDetail(userId, "AuthorizationToken"); - - // Assert - assertNotNull(responseEntity); - assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); - - ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); - assertNotNull(apiResponse); - assertTrue(apiResponse.getSuccess()); - assertNull(apiResponse.getMessage()); - - assertEquals(userServiceRoleDTO, apiResponse.getData()); - - verify(userService, times(1)).getUserDetail(userId); + void getUserDetail_success() throws Exception { + UserServiceRoleDTO dto = new UserServiceRoleDTO(); + when(userService.getUserDetail(anyInt())).thenReturn(dto); + mockMvc.perform(get("/user/getUserDetail") + .param("userId", "1") + .header("Authorization", "Bearer token") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isAccepted()) + .andExpect(jsonPath("$.success").value(true)); } @Test - void testGetUserDetail_Exception() { - // Arrange - Integer userId = 123; - - when(userService.getUserDetail(anyInt())).thenThrow(new RuntimeException("Test exception")); - - // Act - ResponseEntity responseEntity = userController.getUserDetail(userId, "AuthorizationToken"); - - // Assert - assertNotNull(responseEntity); - assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode()); - - ApiResponse apiResponse = (ApiResponse) responseEntity.getBody(); - assertNotNull(apiResponse); - assertFalse(apiResponse.getSuccess()); - assertNotNull(apiResponse.getMessage()); - - assertNull(apiResponse.getData()); - - verify(userService, times(1)).getUserDetail(userId); + void getUserDetail_exception() throws Exception { + when(userService.getUserDetail(anyInt())).thenThrow(new RuntimeException("fail")); + mockMvc.perform(get("/user/getUserDetail") + .param("userId", "1") + .header("Authorization", "Bearer token") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.success").value(false)); } } 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..217ac1eeb --- /dev/null +++ b/src/test/java/com/iemr/flw/service/BadgeServiceTest.java @@ -0,0 +1,91 @@ +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.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; +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.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, ""))); + + 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, "")))); + verify(earnedRepo, never()).save(any()); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/BeneficiaryServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/BeneficiaryServiceImplTest.java new file mode 100644 index 000000000..d6bd0c371 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/BeneficiaryServiceImplTest.java @@ -0,0 +1,773 @@ +package com.iemr.flw.service.impl; + +import com.iemr.flw.domain.identity.*; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.repo.identity.BeneficiaryRepo; +import com.iemr.flw.repo.identity.HouseHoldRepo; +import com.iemr.flw.utils.config.ConfigProperties; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.util.ReflectionTestUtils; + +import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class BeneficiaryServiceImplTest { + + @Mock + private BeneficiaryRepo beneficiaryRepo; + + @Mock + private HouseHoldRepo houseHoldRepo; + + @InjectMocks + private BeneficiaryServiceImpl service; + + private GetBenRequestHandler request; + private RMNCHMBeneficiaryaddress address; + private RMNCHMBeneficiarymapping mapping; + private final String authorization = "Bearer test-token"; + private final Timestamp currentTime = new Timestamp(System.currentTimeMillis()); + + @BeforeEach + void setUp() { + // Set the property value for page size + ReflectionTestUtils.setField(service, "door_to_door_page_size", "10"); + + // Setup common test data + request = new GetBenRequestHandler(); + request.setAshaId(1); + request.setPageNo(0); + request.setUserName("testUser"); + + address = createTestAddress(); + mapping = createTestMapping(); + } + + // getBenData tests + @Test + void testGetBenData_withValidRequest_success() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); +// //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); +// + setupMappingDataMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("\"data\"")); + assertTrue(result.contains("\"pageSize\":10")); + assertTrue(result.contains("\"totalPage\":1")); + + // Verify repository calls + verify(beneficiaryRepo).getUserName(request.getAshaId()); + verify(beneficiaryRepo).getBenDataByUser("testUser", pageRequest); + verify(beneficiaryRepo).getByAddressID(address.getId()); + } + + @Test + void testGetBenData_withDateRange_success() throws Exception { + // Arrange + Timestamp fromDate = Timestamp.valueOf(LocalDate.now().minusDays(7).atStartOfDay()); + Timestamp toDate = Timestamp.valueOf(LocalDate.now().atStartOfDay()); + request.setFromDate(fromDate); + request.setToDate(toDate); + + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataWithinDates("testUser", fromDate, toDate, pageRequest)).thenReturn(page); +// //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + setupMappingDataMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("\"data\"")); + + // Verify repository calls + verify(beneficiaryRepo).getUserName(request.getAshaId()); + verify(beneficiaryRepo).getBenDataWithinDates("testUser", fromDate, toDate, pageRequest); + } + + @Test + void testGetBenData_nullAshaId_throwsException() { + // Arrange + request.setAshaId(null); + + // Act & Assert + Exception exception = assertThrows(Exception.class, () -> { + service.getBenData(request, authorization); + }); + assertEquals("Invalid/missing village details", exception.getMessage()); + } + + @Test + void testGetBenData_invalidPageNo_throwsException() { + // Arrange + request.setPageNo(null); + + // Act & Assert + Exception exception = assertThrows(Exception.class, () -> { + service.getBenData(request, authorization); + }); + assertEquals("Invalid page no", exception.getMessage()); + } + + @Test + void testGetBenData_userNameNotFound_throwsException() { + // Arrange + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn(null); + + // Act & Assert + Exception exception = assertThrows(Exception.class, () -> { + service.getBenData(request, authorization); + }); + assertEquals("Asha details not found, please contact administrator", exception.getMessage()); + + verify(beneficiaryRepo).getUserName(request.getAshaId()); + } + + @Test + void testGetBenData_emptyUserName_throwsException() { + // Arrange + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn(""); + + // Act & Assert + Exception exception = assertThrows(Exception.class, () -> { + service.getBenData(request, authorization); + }); + assertEquals("Asha details not found, please contact administrator", exception.getMessage()); + } + + @Test + void testGetBenData_noResultsFound_returnsNull() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List emptyList = new ArrayList<>(); + Page emptyPage = new PageImpl<>(emptyList, pageRequest, 0); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(emptyPage); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNull(result); + } + + @Test + void testGetBenData_repositoryException_throwsException() { + // Arrange + when(beneficiaryRepo.getUserName(request.getAshaId())).thenThrow(new RuntimeException("Database error")); + + // Act & Assert + Exception exception = assertThrows(Exception.class, () -> { + service.getBenData(request, authorization); + }); + assertEquals("Database error", exception.getMessage()); + } + + @Test + void testGetBenData_nullMapping_skipsProcessing() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(null); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("\"data\":[]")); + verify(beneficiaryRepo).getByAddressID(address.getId()); + } + + @Test + void testGetBenData_withCompleteMapping_success() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); +// //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + setupCompleteEntityMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("\"beneficiaryDetails\"")); + assertTrue(result.contains("\"householdDetails\"")); + assertTrue(result.contains("\"bornbirthDeatils\"")); + + // Verify all entity retrievals + verify(beneficiaryRepo).getDetailsById(mapping.getBenDetailsId()); + verify(beneficiaryRepo).getAccountById(mapping.getBenAccountID()); + verify(beneficiaryRepo).getImageById(mapping.getBenImageId().longValue()); + verify(beneficiaryRepo).getAddressById(mapping.getBenAddressId()); + verify(beneficiaryRepo).getContactById(mapping.getBenContactsId()); + } + + @Test + void testGetBenData_ageCalculation_years() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); +// //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + // Create detail with DOB 25 years ago + RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); + LocalDate birthDate = LocalDate.now().minusYears(25).minusMonths(3); + detail.setDob(Timestamp.valueOf(birthDate.atStartOfDay())); + + when(beneficiaryRepo.getDetailsById(mapping.getBenDetailsId())).thenReturn(detail); + setupOtherEntityMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("Years")); + } + + @Test + void testGetBenData_ageCalculation_months() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + // Create detail with DOB 6 months ago + RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); + LocalDate birthDate = LocalDate.now().minusMonths(6).minusDays(15); + detail.setDob(Timestamp.valueOf(birthDate.atStartOfDay())); + + when(beneficiaryRepo.getDetailsById(mapping.getBenDetailsId())).thenReturn(detail); + setupOtherEntityMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("Months") || result.contains("Month")); + } + + @Test + void testGetBenData_ageCalculation_days() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + // Create detail with DOB 15 days ago + RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); + LocalDate birthDate = LocalDate.now().minusDays(15); + detail.setDob(Timestamp.valueOf(birthDate.atStartOfDay())); + + when(beneficiaryRepo.getDetailsById(mapping.getBenDetailsId())).thenReturn(detail); + setupOtherEntityMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("Days") || result.contains("Day")); + } + + @Test + void testGetBenData_relatedBeneficiaryIds_parsing() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + // Create RMNCH details with related beneficiary IDs + RMNCHBeneficiaryDetailsRmnch rmnchDetails = createTestRMNCHBeneficiaryDetails(); + rmnchDetails.setRelatedBeneficiaryIdsDB("123,456,789"); + + //when(beneficiaryRepo.getDetailsByRegID(mapping.getBenRegId().longValue())).thenReturn(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()); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + verify(beneficiaryRepo).getDetailsByRegID(mapping.getBenRegId().longValue()); + } + + @Test + void testGetBenData_withHealthIdFetch_success() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + setupMappingDataMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("\"data\"")); + // Note: Health ID fetch happens in a try-catch block and doesn't affect the main flow + } + + @Test + void testGetBenData_healthIdFetchException_continuesProcessing() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + setupMappingDataMocks(); + setupHealthDetailsMocks(); + + // Mock ConfigProperties to throw exception + try (MockedStatic configMock = mockStatic(ConfigProperties.class)) { + configMock.when(() -> ConfigProperties.getPropertyByName(anyString())).thenThrow(new RuntimeException("Config error")); + + // Act + String result = service.getBenData(request, authorization); + + // Assert - Should still return valid result despite health ID fetch failure + assertNotNull(result); + assertTrue(result.contains("\"data\"")); + } + } + + @Test + void testGetBenData_getBenHealthDetails_withValidData() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + setupMappingDataMocks(); + + // Setup health details with valid data + Object[] healthIdData = {"test-health-id", "12345"}; + Object[] healthIdNumber = {healthIdData}; + when(beneficiaryRepo.getBenHealthIdNumber(mapping.getBenRegId())).thenReturn(healthIdNumber); + + List healthDetailsList = new ArrayList<>(); + Object[] healthDetails = {"health-id-123", "health-number-456", true}; + healthDetailsList.add(healthDetails); + when(beneficiaryRepo.getBenHealthDetails("test-health-id")).thenReturn(new ArrayList<>(healthDetailsList)); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("\"abhaHealthDetails\"")); + verify(beneficiaryRepo).getBenHealthIdNumber(mapping.getBenRegId()); + verify(beneficiaryRepo).getBenHealthDetails("test-health-id"); + } + + @Test + void testGetBenData_getBenHealthDetails_emptyHealthDetails() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + setupMappingDataMocks(); + + // Setup health details with valid health ID but no health details + Object[] healthIdData = {"test-health-id", "12345"}; + Object[] healthIdNumber = {healthIdData}; + when(beneficiaryRepo.getBenHealthIdNumber(mapping.getBenRegId())).thenReturn(healthIdNumber); + when(beneficiaryRepo.getBenHealthDetails("test-health-id")).thenReturn(new ArrayList<>()); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + verify(beneficiaryRepo).getBenHealthIdNumber(mapping.getBenRegId()); + verify(beneficiaryRepo).getBenHealthDetails("test-health-id"); + } + + @Test + void testGetBenData_getBenHealthDetails_nullHealthIdNumber() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + setupMappingDataMocks(); + + // Setup health details with null health ID number + Object[] healthIdData = {null, "12345"}; + Object[] healthIdNumber = {healthIdData}; + when(beneficiaryRepo.getBenHealthIdNumber(mapping.getBenRegId())).thenReturn(healthIdNumber); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + verify(beneficiaryRepo).getBenHealthIdNumber(mapping.getBenRegId()); + verify(beneficiaryRepo, never()).getBenHealthDetails(anyString()); + } + + @Test + void testGetBenData_nullBenRegId_skipsHealthDetails() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + RMNCHMBeneficiarymapping nullRegIdMapping = createTestMapping(); + nullRegIdMapping.setBenRegId(null); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); +// when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(nullRegIdMapping); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + verify(beneficiaryRepo, never()).getBenHealthIdNumber(any()); + verify(beneficiaryRepo, never()).getDetailsByRegID(anyLong()); + verify(beneficiaryRepo, never()).getBornBirthByRegID(anyLong()); + } + + @Test + void testGetBenData_partialMappingData_handlesNulls() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + // Create mapping with only some fields set + RMNCHMBeneficiarymapping partialMapping = new RMNCHMBeneficiarymapping(); + partialMapping.setBenRegId(BigInteger.valueOf(100L)); + partialMapping.setBenDetailsId(BigInteger.valueOf(101L)); + // Leave other IDs null + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); +// when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(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.getBornBirthByRegID(partialMapping.getBenRegId().longValue())).thenReturn(createTestBornBirthDetails()); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + verify(beneficiaryRepo).getDetailsById(partialMapping.getBenDetailsId()); + verify(beneficiaryRepo, never()).getAccountById(any()); + verify(beneficiaryRepo, never()).getImageById(anyLong()); + verify(beneficiaryRepo, never()).getAddressById(any()); + verify(beneficiaryRepo, never()).getContactById(any()); + } + + @Test + void testGetBenData_addressExceptionHandling_continuesProcessing() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + RMNCHMBeneficiaryaddress problematicAddress = createTestAddress(); + problematicAddress.setId(BigInteger.valueOf(999L)); + + List addressList = Arrays.asList(address, problematicAddress); + Page page = new PageImpl<>(addressList, pageRequest, 2); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + when(beneficiaryRepo.getByAddressID(problematicAddress.getId())).thenThrow(new RuntimeException("Database error")); + + setupMappingDataMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + assertTrue(result.contains("\"data\"")); + // Should have 1 successful result despite one address failing + verify(beneficiaryRepo).getByAddressID(address.getId()); + verify(beneficiaryRepo).getByAddressID(problematicAddress.getId()); + } + + @Test + void testGetBenData_nullDob_skipsAgeCalculation() throws Exception { + // Arrange + PageRequest pageRequest = PageRequest.of(0, 10); + List addressList = Arrays.asList(address); + Page page = new PageImpl<>(addressList, pageRequest, 1); + + when(beneficiaryRepo.getUserName(request.getAshaId())).thenReturn("testUser"); + when(beneficiaryRepo.getBenDataByUser("testUser", pageRequest)).thenReturn(page); + //when(beneficiaryRepo.getByAddressID(address.getId())).thenReturn(mapping); + + // Create detail with null DOB + RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); + detail.setDob(null); + + when(beneficiaryRepo.getDetailsById(mapping.getBenDetailsId())).thenReturn(detail); + setupOtherEntityMocks(); + setupHealthDetailsMocks(); + + // Act + String result = service.getBenData(request, authorization); + + // Assert + assertNotNull(result); + // Should still process successfully without age calculation + assertTrue(result.contains("\"beneficiaryDetails\"")); + } + + // Helper methods to create test data + private RMNCHMBeneficiaryaddress createTestAddress() { + RMNCHMBeneficiaryaddress addr = new RMNCHMBeneficiaryaddress(); + addr.setId(BigInteger.valueOf(1L)); + addr.setVanID(1); + addr.setCreatedBy("testUser"); + addr.setPermCountry("India"); + addr.setPermState("Test State"); + addr.setDistrictnamePerm("Test District"); + addr.setPermSubDistrict("Test Block"); + addr.setVillagenamePerm("Test Village"); + addr.setPermServicePoint("Test Service Point"); + addr.setPermZone("Test Zone"); + addr.setPermAddrLine1("Address Line 1"); + addr.setPermAddrLine2("Address Line 2"); + addr.setPermAddrLine3("Address Line 3"); + addr.setCountyid(1); + addr.setStatePerm(1); + addr.setDistrictidPerm(1); + addr.setPermSubDistrictId(1); + addr.setVillageidPerm(1); + addr.setPermServicePointId(1); + addr.setPermZoneID(1); + return addr; + } + + private RMNCHMBeneficiarymapping createTestMapping() { + RMNCHMBeneficiarymapping map = new RMNCHMBeneficiarymapping(); + map.setBenRegId(BigInteger.valueOf(100L)); + map.setBenDetailsId(BigInteger.valueOf(101L)); + map.setBenAccountID(BigInteger.valueOf(102L)); + map.setBenImageId(BigInteger.valueOf(103L)); + map.setBenAddressId(BigInteger.valueOf(104L)); + map.setBenContactsId(BigInteger.valueOf(105L)); + return map; + } + + private RMNCHMBeneficiarydetail createTestBeneficiaryDetail() { + RMNCHMBeneficiarydetail detail = new RMNCHMBeneficiarydetail(); + detail.setFirstName("John"); + detail.setLastName("Doe"); + detail.setFatherName("Father Name"); + detail.setMotherName("Mother Name"); + detail.setSpousename("Spouse Name"); + detail.setGender("Male"); + detail.setGenderId(1); + detail.setDob(currentTime); + detail.setMaritalstatus("Married"); + detail.setMaritalstatusId(2); + detail.setMarriageDate(currentTime); + detail.setReligion("Hindu"); + detail.setReligionID(BigInteger.valueOf(1L)); + detail.setCommunity("General"); + detail.setCommunityId(1); + detail.setLiteracyStatus("Literate"); + detail.setCreatedBy("testUser"); + return detail; + } + + private RMNCHMBeneficiaryAccount createTestBeneficiaryAccount() { + RMNCHMBeneficiaryAccount account = new RMNCHMBeneficiaryAccount(); + account.setNameOfBank("Test Bank"); + account.setBranchName("Test Branch"); + account.setIfscCode("TEST0001"); + account.setBankAccount("1234567890"); + return account; + } + + private RMNCHMBeneficiaryImage createTestBeneficiaryImage() { + RMNCHMBeneficiaryImage image = new RMNCHMBeneficiaryImage(); + image.setUser_image("base64encodedimage"); + return image; + } + + private RMNCHMBeneficiarycontact createTestBeneficiaryContact() { + RMNCHMBeneficiarycontact contact = new RMNCHMBeneficiarycontact(); + contact.setPreferredPhoneNum("9876543210"); + return contact; + } + + private RMNCHBeneficiaryDetailsRmnch createTestRMNCHBeneficiaryDetails() { + RMNCHBeneficiaryDetailsRmnch rmnchDetails = new RMNCHBeneficiaryDetailsRmnch(); + rmnchDetails.setHouseoldId(200L); + rmnchDetails.setCreatedBy("testUser"); + return rmnchDetails; + } + + private RMNCHHouseHoldDetails createTestHouseHoldDetails() { + RMNCHHouseHoldDetails houseHold = new RMNCHHouseHoldDetails(); + houseHold.setId(200L); + return houseHold; + } + + private RMNCHBornBirthDetails createTestBornBirthDetails() { + RMNCHBornBirthDetails birthDetails = new RMNCHBornBirthDetails(); + birthDetails.setBenRegId(100L); + return birthDetails; + } + + private void setupMappingDataMocks() { + RMNCHMBeneficiarydetail detail = createTestBeneficiaryDetail(); + RMNCHMBeneficiaryAccount account = createTestBeneficiaryAccount(); + RMNCHMBeneficiaryImage image = createTestBeneficiaryImage(); + RMNCHMBeneficiarycontact contact = createTestBeneficiaryContact(); + RMNCHBeneficiaryDetailsRmnch rmnchDetails = createTestRMNCHBeneficiaryDetails(); + RMNCHHouseHoldDetails houseHold = createTestHouseHoldDetails(); + RMNCHBornBirthDetails birthDetails = createTestBornBirthDetails(); + + when(beneficiaryRepo.getDetailsById(mapping.getBenDetailsId())).thenReturn(detail); + when(beneficiaryRepo.getAccountById(mapping.getBenAccountID())).thenReturn(account); + when(beneficiaryRepo.getImageById(mapping.getBenImageId().longValue())).thenReturn(image); + 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.getBornBirthByRegID(mapping.getBenRegId().longValue())).thenReturn(birthDetails); +// when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(houseHold); + when(beneficiaryRepo.getUserIDByUserName("testUser")).thenReturn(123); + } + + private void setupCompleteEntityMocks() { + setupMappingDataMocks(); + } + + private void setupOtherEntityMocks() { + RMNCHMBeneficiaryAccount account = createTestBeneficiaryAccount(); + RMNCHMBeneficiaryImage image = createTestBeneficiaryImage(); + RMNCHMBeneficiarycontact contact = createTestBeneficiaryContact(); + RMNCHBeneficiaryDetailsRmnch rmnchDetails = createTestRMNCHBeneficiaryDetails(); + RMNCHHouseHoldDetails houseHold = createTestHouseHoldDetails(); + RMNCHBornBirthDetails birthDetails = createTestBornBirthDetails(); + + when(beneficiaryRepo.getAccountById(mapping.getBenAccountID())).thenReturn(account); + when(beneficiaryRepo.getImageById(mapping.getBenImageId().longValue())).thenReturn(image); + 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.getBornBirthByRegID(mapping.getBenRegId().longValue())).thenReturn(birthDetails); +// when(houseHoldRepo.getByHouseHoldID(rmnchDetails.getHouseoldId())).thenReturn(houseHold); + when(beneficiaryRepo.getUserIDByUserName("testUser")).thenReturn(123); + } + + private void setupHealthDetailsMocks() { + Object[] healthIdData = {"test-health-id", "12345"}; + Object[] healthIdNumber = {healthIdData}; + when(beneficiaryRepo.getBenHealthIdNumber(mapping.getBenRegId())).thenReturn(healthIdNumber); + when(beneficiaryRepo.getBenHealthDetails("test-health-id")).thenReturn(new ArrayList<>()); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/CbacServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/CbacServiceImplTest.java new file mode 100644 index 000000000..b8b972cba --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/CbacServiceImplTest.java @@ -0,0 +1,142 @@ +package com.iemr.flw.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.iemr.flw.domain.identity.CbacAdditionalDetails; +import com.iemr.flw.domain.identity.CbacDetails; +import com.iemr.flw.dto.identity.CbacDTO; +import com.iemr.flw.dto.identity.CbacStatus; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.repo.identity.BeneficiaryRepo; +import com.iemr.flw.repo.identity.CbacAdditionalDetailRepo; +import com.iemr.flw.repo.identity.CbacRepo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; + +import java.lang.reflect.Field; +import java.math.BigInteger; +import java.sql.Timestamp; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class CbacServiceImplTest { + @Mock + private CbacRepo cbacRepo; + @Mock + private CbacAdditionalDetailRepo cbacAddRepo; + @Mock + private BeneficiaryRepo beneficiaryRepo; + @Mock + private ObjectMapper mapper; + @Mock + private ModelMapper modelMapper; + @InjectMocks + private CbacServiceImpl cbacService; + + @BeforeEach + void setUp() throws Exception { + Field field = CbacServiceImpl.class.getDeclaredField("cbac_page_size"); + field.setAccessible(true); + field.set(cbacService, "10"); + } + + @Test + void getByUserId_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"); + int pageNo = 0; + when(dto.getAshaId()).thenReturn(ashaId); + when(dto.getFromDate()).thenReturn(fromDate); + when(dto.getToDate()).thenReturn(toDate); + when(dto.getPageNo()).thenReturn(pageNo); + when(beneficiaryRepo.getUserName(ashaId)).thenReturn("user1"); + int pageSize = 10; + PageRequest pageRequest = PageRequest.of(pageNo, pageSize); + // Create CbacDetails with proper IDs + CbacDetails cbacDetails1 = new CbacDetails(); + cbacDetails1.setBeneficiaryRegId(100L); + cbacDetails1.setCbacDetailsId(1L); + CbacDetails cbacDetails2 = new CbacDetails(); + cbacDetails2.setBeneficiaryRegId(200L); + cbacDetails2.setCbacDetailsId(2L); + List cbacDetailsList = Arrays.asList(cbacDetails1, cbacDetails2); + Page cbacPage = new org.springframework.data.domain.PageImpl<>(cbacDetailsList, pageRequest, cbacDetailsList.size()); + when(cbacRepo.getAllByCreatedBy("user1", fromDate, toDate, pageRequest)).thenReturn(cbacPage); + when(beneficiaryRepo.getBenIdFromRegID(anyLong())).thenReturn(BigInteger.valueOf(1L)); + when(cbacAddRepo.findCbacAdditionalDetail(anyLong())).thenReturn(new CbacAdditionalDetails()); + when(mapper.convertValue(any(CbacDetails.class), eq(CbacDTO.class))).thenReturn(new CbacDTO()); + String json = cbacService.getByUserId(dto); + assertNotNull(json); + @SuppressWarnings("unchecked") + Map result = new Gson().fromJson(json, Map.class); + assertTrue(result.containsKey("data")); + assertTrue(result.containsKey("pageSize")); + assertTrue(result.containsKey("totalPage")); + } + + @Test + void getByUserId_exception() { + GetBenRequestHandler dto = mock(GetBenRequestHandler.class); + when(beneficiaryRepo.getUserName(anyInt())).thenThrow(new RuntimeException("fail")); + String json = cbacService.getByUserId(dto); + assertNull(json); + } + + @Test + void save_new_success() { + CbacDTO dto = new CbacDTO(); + dto.setBeneficiaryId(1L); + java.sql.Timestamp createdDate = java.sql.Timestamp.valueOf("2023-08-16 00:00:00"); + dto.setCreatedDate(createdDate); + List dtos = Collections.singletonList(dto); + when(beneficiaryRepo.getRegIDFromBenId(1L)).thenReturn(2L); + CbacDetails cbacDetails = new CbacDetails(); + cbacDetails.setCreatedDate(createdDate); + when(modelMapper.map(dto, CbacDetails.class)).thenReturn(cbacDetails); + when(cbacRepo.findCbacDetailsByBeneficiaryRegIdAndCreatedDate(2L, createdDate)).thenReturn(null); + CbacDetails savedCbac = new CbacDetails(); + savedCbac.setCbacDetailsId(5L); + when(cbacRepo.save(any(CbacDetails.class))).thenReturn(savedCbac); + CbacAdditionalDetails cbacAdditionalDetails = new CbacAdditionalDetails(); + when(modelMapper.map(dto, CbacAdditionalDetails.class)).thenReturn(cbacAdditionalDetails); + when(cbacAddRepo.saveAll(anyList())).thenReturn(Collections.emptyList()); + String json = cbacService.save(dtos); + assertNotNull(json); + CbacStatus[] arr = new Gson().fromJson(json, CbacStatus[].class); + assertEquals(1, arr.length); + assertEquals("Success", arr[0].getStatus()); + } + + @Test + void save_existing_fail() { + CbacDTO dto = new CbacDTO(); + dto.setBeneficiaryId(1L); + java.sql.Timestamp createdDate = java.sql.Timestamp.valueOf("2023-08-16 00:00:00"); + dto.setCreatedDate(createdDate); + List dtos = Collections.singletonList(dto); + when(beneficiaryRepo.getRegIDFromBenId(1L)).thenReturn(2L); + CbacDetails cbacDetails = new CbacDetails(); + cbacDetails.setCreatedDate(createdDate); + when(modelMapper.map(dto, CbacDetails.class)).thenReturn(cbacDetails); + CbacDetails existing = new CbacDetails(); + when(cbacRepo.findCbacDetailsByBeneficiaryRegIdAndCreatedDate(2L, createdDate)).thenReturn(existing); + String json = cbacService.save(dtos); + assertNotNull(json); + CbacStatus[] arr = new Gson().fromJson(json, CbacStatus[].class); + assertEquals(1, arr.length); + assertEquals("Fail", arr[0].getStatus()); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/ChildCareServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/ChildCareServiceImplTest.java new file mode 100644 index 000000000..c9fda28ad --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/ChildCareServiceImplTest.java @@ -0,0 +1,745 @@ +//package com.iemr.flw.service.impl; +// +//import com.iemr.flw.domain.iemr.*; +//import com.iemr.flw.dto.identity.GetBenRequestHandler; +//import com.iemr.flw.dto.iemr.*; +//import com.iemr.flw.repo.identity.BeneficiaryRepo; +//import com.iemr.flw.repo.iemr.*; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.extension.ExtendWith; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.junit.jupiter.MockitoExtension; +// +//import java.math.BigInteger; +//import java.sql.Timestamp; +//import java.time.Instant; +//import java.util.*; +// +//import static org.junit.jupiter.api.Assertions.*; +//import static org.mockito.ArgumentMatchers.*; +//import static org.mockito.Mockito.*; +// +//@ExtendWith(MockitoExtension.class) +//class ChildCareServiceImplTest { +// +// @Mock +// private HbycRepo hbycRepo; +// @Mock +// private BeneficiaryRepo beneficiaryRepo; +// @Mock +// private HbncVisitRepo hbncVisitRepo; +// @Mock +// private HbncVisitCardRepo hbncVisitCardRepo; +// @Mock +// private HbncPart1Repo hbncPart1Repo; +// @Mock +// private HbncPart2repo hbncPart2repo; +// @Mock +// private IncentivesRepo incentivesRepo; +// @Mock +// private UserServiceRoleRepo userRepo; +// @Mock +// private IncentiveRecordRepo recordRepo; +// @Mock +// private ChildVaccinationRepo childVaccinationRepo; +// @Mock +// private VaccineRepo vaccineRepo; +// +// @InjectMocks +// private ChildCareServiceImpl service; +// +// private Timestamp currentTime; +// +// @BeforeEach +// 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); +// } +// +// // getChildVaccinationDetails tests +// @Test +// void testGetChildVaccinationDetails_success() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// String userName = "testUser"; +// ChildVaccination vaccination = new ChildVaccination(); +// vaccination.setId(1L); +// vaccination.setBeneficiaryRegId(100L); +// vaccination.setVaccineName("BCG"); +// +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); +// when(childVaccinationRepo.getChildVaccinationDetails(userName, dto.getFromDate(), dto.getToDate())) +// .thenReturn(Arrays.asList(vaccination)); +// when(beneficiaryRepo.getBenIdFromRegID(vaccination.getBeneficiaryRegId())) +// .thenReturn(BigInteger.valueOf(123L)); +// +// // Act +// List result = service.getChildVaccinationDetails(dto); +// +// // Assert +// assertNotNull(result); +// assertEquals(1, result.size()); +// assertEquals(123L, result.get(0).getBeneficiaryId()); +// verify(beneficiaryRepo).getUserName(dto.getAshaId()); +// verify(childVaccinationRepo).getChildVaccinationDetails(userName, dto.getFromDate(), dto.getToDate()); +// verify(beneficiaryRepo).getBenIdFromRegID(vaccination.getBeneficiaryRegId()); +// } +// +// @Test +// void testGetChildVaccinationDetails_exception() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenThrow(new RuntimeException("Database error")); +// +// // Act +// List result = service.getChildVaccinationDetails(dto); +// +// // Assert +// assertNull(result); +// } +// +// // saveChildVaccinationDetails tests +// @Test +// void testSaveChildVaccinationDetails_newRecord_success() { +// // Arrange +// ChildVaccinationDTO dto = createChildVaccinationDTO(); +// Long benRegId = 100L; +// ChildVaccination savedVaccination = new ChildVaccination(); +// savedVaccination.setId(1L); +// savedVaccination.setBeneficiaryRegId(benRegId); +// savedVaccination.setVaccineId(dto.getVaccineId()); +// savedVaccination.setCreatedBy(dto.getCreatedBy()); +// savedVaccination.setCreatedDate(dto.getCreatedDate()); +// +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBeneficiaryId())).thenReturn(benRegId); +// when(childVaccinationRepo.findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName( +// benRegId, dto.getCreatedDate(), dto.getVaccineName())) +// .thenReturn(null); +// when(childVaccinationRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedVaccination)); +// // Mocks for checkAndAddIncentives method +// when(beneficiaryRepo.getBenIdFromRegID(benRegId)).thenReturn(BigInteger.valueOf(dto.getBeneficiaryId())); +// when(userRepo.getUserIdByName(dto.getCreatedBy())).thenReturn(1); +// when(vaccineRepo.getImmunizationServiceIdByVaccineId(dto.getVaccineId().shortValue())).thenReturn(1); +// // Additional mocks for incentive logic (immunizationServiceId < 6) +// IncentiveActivity incentiveActivity = new IncentiveActivity(); +// incentiveActivity.setId(1L); +// incentiveActivity.setName("IMMUNIZATION_0_1"); +// incentiveActivity.setRate(100); +// 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.save(any(IncentiveActivityRecord.class))).thenReturn(new IncentiveActivityRecord()); +// +// // Act +// String result = service.saveChildVaccinationDetails(Arrays.asList(dto)); +// +// // Assert +// assertEquals("No of child vaccination details saved: 1", result); +// verify(beneficiaryRepo).getRegIDFromBenId(dto.getBeneficiaryId()); +// verify(childVaccinationRepo).findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName( +// benRegId, dto.getCreatedDate(), dto.getVaccineName()); +// verify(childVaccinationRepo).saveAll(anyList()); +// } +// +// @Test +// void testSaveChildVaccinationDetails_existingRecord_success() { +// // Arrange +// ChildVaccinationDTO dto = createChildVaccinationDTO(); +// Long benRegId = 100L; +// ChildVaccination existingVaccination = new ChildVaccination(); +// existingVaccination.setId(5L); +// existingVaccination.setBeneficiaryRegId(benRegId); +// existingVaccination.setVaccineId(dto.getVaccineId()); +// existingVaccination.setCreatedBy(dto.getCreatedBy()); +// existingVaccination.setCreatedDate(dto.getCreatedDate()); +// +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBeneficiaryId())).thenReturn(benRegId); +// when(childVaccinationRepo.findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName( +// benRegId, dto.getCreatedDate(), dto.getVaccineName())) +// .thenReturn(existingVaccination); +// when(childVaccinationRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingVaccination)); +// // Mocks for checkAndAddIncentives method +// when(beneficiaryRepo.getBenIdFromRegID(benRegId)).thenReturn(BigInteger.valueOf(dto.getBeneficiaryId())); +// when(userRepo.getUserIdByName(dto.getCreatedBy())).thenReturn(1); +// when(vaccineRepo.getImmunizationServiceIdByVaccineId(dto.getVaccineId().shortValue())).thenReturn(1); +// // Additional mocks for incentive logic (immunizationServiceId < 6) +// IncentiveActivity incentiveActivity = new IncentiveActivity(); +// incentiveActivity.setId(1L); +// incentiveActivity.setName("IMMUNIZATION_0_1"); +// incentiveActivity.setRate(100); +// 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.save(any(IncentiveActivityRecord.class))).thenReturn(new IncentiveActivityRecord()); +// +// // Act +// String result = service.saveChildVaccinationDetails(Arrays.asList(dto)); +// +// // Assert +// assertEquals("No of child vaccination details saved: 1", result); +// verify(beneficiaryRepo).getRegIDFromBenId(dto.getBeneficiaryId()); +// verify(childVaccinationRepo).findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName( +// benRegId, dto.getCreatedDate(), dto.getVaccineName()); +// verify(childVaccinationRepo).saveAll(anyList()); +// } +// +// @Test +// void testSaveChildVaccinationDetails_withIncentives_firstYear_success() { +// // Arrange +// ChildVaccinationDTO dto = createChildVaccinationDTO(); +// dto.setVaccineId(1); // First year vaccine +// Long benRegId = 100L; +// ChildVaccination savedVaccination = new ChildVaccination(); +// savedVaccination.setId(1L); +// savedVaccination.setBeneficiaryRegId(benRegId); +// savedVaccination.setVaccineId(1); +// savedVaccination.setCreatedDate(currentTime); +// savedVaccination.setCreatedBy("testUser"); +// +// IncentiveActivity incentiveActivity = new IncentiveActivity(); +// incentiveActivity.setId(1L); +// incentiveActivity.setRate(100); +// +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBeneficiaryId())).thenReturn(benRegId); +// when(childVaccinationRepo.findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName( +// benRegId, dto.getCreatedDate(), dto.getVaccineName())) +// .thenReturn(null); +// when(childVaccinationRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedVaccination)); +// when(beneficiaryRepo.getBenIdFromRegID(benRegId)).thenReturn(BigInteger.valueOf(123L)); +// when(userRepo.getUserIdByName("testUser")).thenReturn(1); +// when(vaccineRepo.getImmunizationServiceIdByVaccineId((short) 1)).thenReturn(1); // First year vaccine +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("IMMUNIZATION_0_1", "IMMUNIZATION")) +// .thenReturn(incentiveActivity); +// when(childVaccinationRepo.getFirstYearVaccineCountForBenId(benRegId)).thenReturn(5); +// when(childVaccinationRepo.getFirstYearVaccineCount()).thenReturn(5); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(1L, currentTime, 123L)) +// .thenReturn(null); +// +// // Act +// String result = service.saveChildVaccinationDetails(Arrays.asList(dto)); +// +// // Assert +// assertEquals("No of child vaccination details saved: 1", result); +// verify(recordRepo).save(any(IncentiveActivityRecord.class)); +// } +// +// @Test +// void testSaveChildVaccinationDetails_withIncentives_secondYear_success() { +// // Arrange +// ChildVaccinationDTO dto = createChildVaccinationDTO(); +// dto.setVaccineId(7); // Second year vaccine +// Long benRegId = 100L; +// ChildVaccination savedVaccination = new ChildVaccination(); +// savedVaccination.setId(1L); +// savedVaccination.setBeneficiaryRegId(benRegId); +// savedVaccination.setVaccineId(7); +// savedVaccination.setCreatedDate(currentTime); +// savedVaccination.setCreatedBy("testUser"); +// +// IncentiveActivity incentiveActivity = new IncentiveActivity(); +// incentiveActivity.setId(2L); +// incentiveActivity.setRate(150); +// +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBeneficiaryId())).thenReturn(benRegId); +// when(childVaccinationRepo.findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName( +// benRegId, dto.getCreatedDate(), dto.getVaccineName())) +// .thenReturn(null); +// when(childVaccinationRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedVaccination)); +// when(beneficiaryRepo.getBenIdFromRegID(benRegId)).thenReturn(BigInteger.valueOf(123L)); +// when(userRepo.getUserIdByName("testUser")).thenReturn(1); +// when(vaccineRepo.getImmunizationServiceIdByVaccineId((short) 7)).thenReturn(7); // Second year vaccine +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("IMMUNIZATION_1_2", "IMMUNIZATION")) +// .thenReturn(incentiveActivity); +// when(childVaccinationRepo.getSecondYearVaccineCountForBenId(benRegId)).thenReturn(3); +// when(childVaccinationRepo.getSecondYearVaccineCount()).thenReturn(3); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(2L, currentTime, 123L)) +// .thenReturn(null); +// +// // Act +// String result = service.saveChildVaccinationDetails(Arrays.asList(dto)); +// +// // Assert +// assertEquals("No of child vaccination details saved: 1", result); +// verify(recordRepo).save(any(IncentiveActivityRecord.class)); +// } +// +// @Test +// void testSaveChildVaccinationDetails_withIncentives_fifthYear_success() { +// // Arrange +// ChildVaccinationDTO dto = createChildVaccinationDTO(); +// dto.setVaccineId(8); // Fifth year vaccine +// Long benRegId = 100L; +// ChildVaccination savedVaccination = new ChildVaccination(); +// savedVaccination.setId(1L); +// savedVaccination.setBeneficiaryRegId(benRegId); +// savedVaccination.setVaccineId(8); +// savedVaccination.setCreatedDate(currentTime); +// savedVaccination.setCreatedBy("testUser"); +// +// IncentiveActivity incentiveActivity = new IncentiveActivity(); +// incentiveActivity.setId(3L); +// incentiveActivity.setRate(200); +// +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBeneficiaryId())).thenReturn(benRegId); +// when(childVaccinationRepo.findChildVaccinationByBeneficiaryRegIdAndCreatedDateAndVaccineName( +// benRegId, dto.getCreatedDate(), dto.getVaccineName())) +// .thenReturn(null); +// when(childVaccinationRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedVaccination)); +// when(beneficiaryRepo.getBenIdFromRegID(benRegId)).thenReturn(BigInteger.valueOf(123L)); +// when(userRepo.getUserIdByName("testUser")).thenReturn(1); +// when(vaccineRepo.getImmunizationServiceIdByVaccineId((short) 8)).thenReturn(8); // Fifth year vaccine +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("IMMUNIZATION_5", "IMMUNIZATION")) +// .thenReturn(incentiveActivity); +// when(childVaccinationRepo.checkDptVaccinatedUser(benRegId)).thenReturn(1); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(3L, currentTime, 123L)) +// .thenReturn(null); +// +// // Act +// String result = service.saveChildVaccinationDetails(Arrays.asList(dto)); +// +// // Assert +// assertEquals("No of child vaccination details saved: 1", result); +// verify(recordRepo).save(any(IncentiveActivityRecord.class)); +// } +// +// @Test +// void testSaveChildVaccinationDetails_exception() { +// // Arrange +// ChildVaccinationDTO dto = createChildVaccinationDTO(); +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBeneficiaryId())) +// .thenThrow(new RuntimeException("Database error")); +// +// // Act +// String result = service.saveChildVaccinationDetails(Arrays.asList(dto)); +// +// // Assert +// assertNull(result); +// } +// +// // getAllChildVaccines tests +// @Test +// void testGetAllChildVaccines_success() { +// // Arrange +// String category = "CHILD"; +// Vaccine vaccine1 = createVaccine("Birth Dose Vaccines"); +// Vaccine vaccine2 = createVaccine("6 Weeks Vaccines"); +// Vaccine vaccine3 = createVaccine("10 Weeks Vaccines"); +// Vaccine vaccine4 = createVaccine("14 Weeks Vaccines"); +// Vaccine vaccine5 = createVaccine("9-12 Months"); +// Vaccine vaccine6 = createVaccine("16-24 Months Vaccines"); +// Vaccine vaccine7 = createVaccine("5-6 Years Vaccine"); +// Vaccine vaccine8 = createVaccine("10 Years Vaccine"); +// Vaccine vaccine9 = createVaccine("16 Years Vaccine"); +// Vaccine vaccine10 = createVaccine("Unknown Category"); +// +// when(vaccineRepo.getAllByCategory(category)).thenReturn(Arrays.asList( +// vaccine1, vaccine2, vaccine3, vaccine4, vaccine5, +// vaccine6, vaccine7, vaccine8, vaccine9, vaccine10 +// )); +// +// // Act +// List result = service.getAllChildVaccines(category); +// +// // Assert +// assertNotNull(result); +// assertEquals(10, result.size()); +// +// // Verify immunization service mappings +// assertEquals("BIRTH", result.get(0).getImmunizationService()); +// assertEquals("WEEK_6", result.get(1).getImmunizationService()); +// assertEquals("WEEK_10", result.get(2).getImmunizationService()); +// assertEquals("WEEK_14", result.get(3).getImmunizationService()); +// assertEquals("MONTH_9_12", result.get(4).getImmunizationService()); +// assertEquals("MONTH_16_24", result.get(5).getImmunizationService()); +// assertEquals("YEAR_5_6", result.get(6).getImmunizationService()); +// assertEquals("YEAR_10", result.get(7).getImmunizationService()); +// assertEquals("YEAR_16", result.get(8).getImmunizationService()); +// assertEquals("CATCH_UP", result.get(9).getImmunizationService()); +// +// verify(vaccineRepo).getAllByCategory(category); +// } +// +// @Test +// void testGetAllChildVaccines_exception() { +// // Arrange +// String category = "CHILD"; +// when(vaccineRepo.getAllByCategory(category)).thenThrow(new RuntimeException("Database error")); +// +// // Act +// List result = service.getAllChildVaccines(category); +// +// // Assert +// assertNull(result); +// } +// +// // 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); +// dto.setFromDate(currentTime); +// dto.setToDate(currentTime); +// 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); +// dto.setVaccineName("BCG"); +// dto.setVaccineId(1); +// dto.setCreatedDate(currentTime); +// dto.setCreatedBy("testUser"); +// dto.setLastModDate(currentTime); +// dto.setModifiedBy("testUser"); +// return dto; +// } +// +// private Vaccine createVaccine(String immunizationService) { +// Vaccine vaccine = new Vaccine(); +// vaccine.setVaccineId((short) 1); +// vaccine.setVaccineName("Test Vaccine"); +// vaccine.setImmunizationService(immunizationService); +// return vaccine; +// } +//} diff --git a/src/test/java/com/iemr/flw/service/impl/ChildServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/ChildServiceImplTest.java new file mode 100644 index 000000000..dba027636 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/ChildServiceImplTest.java @@ -0,0 +1,110 @@ +package com.iemr.flw.service.impl; + +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.ChildRegister; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.ChildRegisterDTO; +import com.iemr.flw.repo.identity.BeneficiaryRepo; +import com.iemr.flw.repo.iemr.ChildRegisterRepo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class ChildServiceImplTest { + @Mock + private ChildRegisterRepo childRepo; + @Mock + private BeneficiaryRepo beneficiaryRepo; + @Mock + private ModelMapper modelMapper; + @InjectMocks + private ChildServiceImpl childService; + + @BeforeEach + void setUp() { + childService.modelMapper = modelMapper; + } + + @Test + void getByUserId_success() { + GetBenRequestHandler dto = mock(GetBenRequestHandler.class); + Integer ashaId = 123; + java.sql.Timestamp fromDate = java.sql.Timestamp.valueOf("2023-01-01 00:00:00"); + java.sql.Timestamp toDate = java.sql.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"); + List childList = Arrays.asList(new ChildRegister(), new ChildRegister()); + when(childRepo.getChildDetailsForUser(eq("user1"), eq(fromDate), eq(toDate))).thenReturn(childList); + when(modelMapper.map(any(ChildRegister.class), eq(ChildRegisterDTO.class))).thenReturn(new ChildRegisterDTO()); + String json = childService.getByUserId(dto); + assertNotNull(json); + ChildRegisterDTO[] arr = new Gson().fromJson(json, ChildRegisterDTO[].class); + assertEquals(2, arr.length); + } + + @Test + void getByUserId_exception() { + GetBenRequestHandler dto = mock(GetBenRequestHandler.class); + Integer ashaId = 123; + when(dto.getAshaId()).thenReturn(ashaId); + when(beneficiaryRepo.getUserName(anyInt())).thenThrow(new RuntimeException("fail")); + String json = childService.getByUserId(dto); + assertNull(json); + } + + @Test + void save_newChild_success() throws Exception { + ChildRegisterDTO dto = new ChildRegisterDTO(); + dto.setBenId(1L); + java.sql.Timestamp createdDate = java.sql.Timestamp.valueOf("2023-01-01 00:00:00"); + dto.setCreatedDate(createdDate); + when(childRepo.findChildRegisterByBenIdAndCreatedDate(anyLong(), any(java.sql.Timestamp.class))).thenReturn(null); + doAnswer(invocation -> { + List list = invocation.getArgument(0); + assertEquals(1, list.size()); + return null; + }).when(childRepo).saveAll(anyList()); + // modelMapper.map returns void for (ChildRegisterDTO, ChildRegister) + // No need to stub modelMapper.map for void methods; Mockito does nothing by default + String result = childService.save(Collections.singletonList(dto)); + assertTrue(result.contains("no of child details saved: 1")); + } + + @Test + void save_existingChild_success() throws Exception { + ChildRegisterDTO dto = new ChildRegisterDTO(); + dto.setBenId(2L); + java.sql.Timestamp createdDate = java.sql.Timestamp.valueOf("2023-01-02 00:00:00"); + dto.setCreatedDate(createdDate); + ChildRegister existing = new ChildRegister(); + existing.setId(99L); + when(childRepo.findChildRegisterByBenIdAndCreatedDate(anyLong(), any(java.sql.Timestamp.class))).thenReturn(existing); + doNothing().when(modelMapper).map(any(ChildRegisterDTO.class), any(ChildRegister.class)); + String result = childService.save(Collections.singletonList(dto)); + assertTrue(result.contains("no of child details saved: 1")); + } + + @Test + void save_exception() throws Exception { + ChildRegisterDTO dto = new ChildRegisterDTO(); + dto.setBenId(3L); + java.sql.Timestamp createdDate = java.sql.Timestamp.valueOf("2023-01-03 00:00:00"); + dto.setCreatedDate(createdDate); + when(childRepo.findChildRegisterByBenIdAndCreatedDate(anyLong(), any(java.sql.Timestamp.class))).thenThrow(new RuntimeException("fail")); + assertThrows(Exception.class, () -> childService.save(Collections.singletonList(dto))); + } +} 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 c948d2f98..c7133a969 100644 --- a/src/test/java/com/iemr/flw/service/impl/CoupleServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/CoupleServiceImplTest.java @@ -1,142 +1,685 @@ -package com.iemr.flw.service.impl; - -import com.iemr.flw.domain.iemr.EligibleCoupleRegister; -import com.iemr.flw.domain.iemr.EligibleCoupleTracking; -import com.iemr.flw.dto.identity.GetBenRequestHandler; -import com.iemr.flw.dto.iemr.EligibleCoupleDTO; -import com.iemr.flw.dto.iemr.EligibleCoupleTrackingDTO; -import com.iemr.flw.repo.iemr.EligibleCoupleRegisterRepo; -import com.iemr.flw.repo.iemr.EligibleCoupleTrackingRepo; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -class CoupleServiceImplTest { - - @Mock - private EligibleCoupleRegisterRepo eligibleCoupleRegisterRepo; - - @Mock - private EligibleCoupleTrackingRepo eligibleCoupleTrackingRepo; - - @InjectMocks - private CoupleServiceImpl coupleService; - - @BeforeEach - void setUp() { - MockitoAnnotations.initMocks(this); - } - -// @Test -// void testRegisterEligibleCouple_Success() { -// // Arrange -// List eligibleCoupleDTOs = new ArrayList<>(); -// eligibleCoupleDTOs.add(new EligibleCoupleDTO()); +//package com.iemr.flw.service.impl; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.google.gson.Gson; +//import com.iemr.flw.domain.iemr.*; +//import com.iemr.flw.dto.identity.GetBenRequestHandler; +//import com.iemr.flw.dto.iemr.EligibleCoupleDTO; +//import com.iemr.flw.dto.iemr.EligibleCoupleTrackingDTO; +//import com.iemr.flw.repo.identity.BeneficiaryRepo; +//import com.iemr.flw.repo.iemr.*; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.extension.ExtendWith; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.junit.jupiter.MockitoExtension; +//import org.modelmapper.ModelMapper; +// +//import java.lang.reflect.Field; +//import java.sql.Timestamp; +//import java.time.LocalDateTime; +//import java.util.*; +// +//import static org.junit.jupiter.api.Assertions.*; +//import static org.mockito.ArgumentMatchers.*; +//import static org.mockito.Mockito.*; +// +//@ExtendWith(MockitoExtension.class) +//class CoupleServiceImplTest { +// @Mock +// private EligibleCoupleRegisterRepo eligibleCoupleRegisterRepo; +// @Mock +// private EligibleCoupleTrackingRepo eligibleCoupleTrackingRepo; +// @Mock +// private IncentivesRepo incentivesRepo; +// @Mock +// private UserServiceRoleRepo userRepo; +// @Mock +// private IncentiveRecordRepo recordRepo; +// @Mock +// private BeneficiaryRepo beneficiaryRepo; +// @Mock +// private ObjectMapper mapper; +// @Mock +// private ModelMapper modelMapper; +// @InjectMocks +// private CoupleServiceImpl coupleService; +// +// @BeforeEach +// void setUp() throws Exception { +// // Set up ObjectMapper and ModelMapper fields using reflection +// Field mapperField = CoupleServiceImpl.class.getDeclaredField("mapper"); +// mapperField.setAccessible(true); +// mapperField.set(coupleService, mapper); +// +// Field modelMapperField = CoupleServiceImpl.class.getDeclaredField("modelMapper"); +// modelMapperField.setAccessible(true); +// modelMapperField.set(coupleService, modelMapper); +// } +// +// @Test +// void registerEligibleCouple_existingECRWithIncentives_success() { +// // Arrange +// EligibleCoupleDTO dto = new EligibleCoupleDTO(); +// dto.setBenId(1L); +// dto.setNumLiveChildren(1); +// dto.setMarriageFirstChildGap(3); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); // // EligibleCoupleRegister existingECR = new EligibleCoupleRegister(); -// when(eligibleCoupleRegisterRepo.getECRWithBen(anyLong())).thenReturn(existingECR); +// existingECR.setId(1L); +// existingECR.setNumLiveChildren(0); +// +// IncentiveActivity activity = new IncentiveActivity(); +// activity.setId(1L); +// activity.getName(); +// activity.setRate(100); +// +// 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())) +// .thenReturn(null); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); // // // Act -// String result = coupleService.registerEligibleCouple(eligibleCoupleDTOs); +// String result = coupleService.registerEligibleCouple(dtoList); // // // Assert // assertNotNull(result); -// assertTrue(result.contains("already exists")); +// 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(userRepo).getUserIdByName("testUser"); +// verify(eligibleCoupleRegisterRepo).saveAll(anyList()); +// verify(recordRepo).saveAll(anyList()); +// verify(modelMapper).map(dto, existingECR); +// } +// +// @Test +// void registerEligibleCouple_secondChildGapIncentive_success() { +// // Arrange +// EligibleCoupleDTO dto = new EligibleCoupleDTO(); +// dto.setBenId(1L); +// dto.setNumLiveChildren(2); +// dto.setMarriageFirstChildGap(2); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); +// +// EligibleCoupleRegister existingECR = new EligibleCoupleRegister(); +// existingECR.setId(1L); +// existingECR.setNumLiveChildren(1); +// +// IncentiveActivity activity = new IncentiveActivity(); +// activity.setId(2L); +// activity.setName("Second child gap"); +// activity.setRate(200); // -// verify(eligibleCoupleRegisterRepo, times(1)).getECRWithBen(anyLong()); -// verify(eligibleCoupleRegisterRepo, never()).save(anyList()); +// 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())) +// .thenReturn(null); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// +// // Act +// String result = coupleService.registerEligibleCouple(dtoList); +// +// // Assert +// assertNotNull(result); +// assertEquals("no of ecr details saved: 1", result); +// verify(incentivesRepo).findIncentiveMasterByNameAndGroup("1st_2nd_CHILD_GAP", "FAMILY PLANNING"); // } // // @Test -// void testRegisterEligibleCouple_Success_NewECR() { +// void registerEligibleCouple_newECR_success() { // // Arrange -// List eligibleCoupleDTOs = new ArrayList<>(); -// eligibleCoupleDTOs.add(new EligibleCoupleDTO()); +// EligibleCoupleDTO dto = new EligibleCoupleDTO(); +// dto.setBenId(1L); +// dto.setNumLiveChildren(0); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); // -// when(eligibleCoupleRegisterRepo.getECRWithBen(anyLong())).thenReturn(null); +// EligibleCoupleRegister newECR = new EligibleCoupleRegister(); +// newECR.setId(null); +// +// when(eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(1L)).thenReturn(null); +// when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(newECR)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// doAnswer(invocation -> { +// EligibleCoupleDTO source = invocation.getArgument(0); +// EligibleCoupleRegister target = invocation.getArgument(1); +// target.setCreatedBy(source.getCreatedBy()); +// return null; +// }).when(modelMapper).map(eq(dto), any(EligibleCoupleRegister.class)); // // // Act -// String result = coupleService.registerEligibleCouple(eligibleCoupleDTOs); +// String result = coupleService.registerEligibleCouple(dtoList); // // // Assert // assertNotNull(result); -// assertTrue(result.contains("Eligible Couple Register Details Saved!")); +// assertEquals("no of ecr details saved: 1", result); +// verify(eligibleCoupleRegisterRepo).findEligibleCoupleRegisterByBenId(1L); +// verify(eligibleCoupleRegisterRepo).saveAll(anyList()); +// verify(recordRepo).saveAll(anyList()); +// verify(modelMapper).map(eq(dto), any(EligibleCoupleRegister.class)); +// } +// +// @Test +// void registerEligibleCouple_existingRecord_noIncentive() { +// // Arrange +// EligibleCoupleDTO dto = new EligibleCoupleDTO(); +// dto.setBenId(1L); +// dto.setNumLiveChildren(1); +// dto.setMarriageFirstChildGap(3); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); // -// verify(eligibleCoupleRegisterRepo, times(1)).getECRWithBen(anyLong()); -// verify(eligibleCoupleRegisterRepo, times(1)).save(anyList()); +// EligibleCoupleRegister existingECR = new EligibleCoupleRegister(); +// existingECR.setId(1L); +// existingECR.setNumLiveChildren(0); +// +// IncentiveActivity activity = new IncentiveActivity(); +// activity.setId(1L); +// activity.setName("Marriage gap"); +// activity.setRate(100); +// +// IncentiveActivityRecord existingRecord = new IncentiveActivityRecord(); +// +// 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())) +// .thenReturn(existingRecord); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// +// // Act +// String result = coupleService.registerEligibleCouple(dtoList); +// +// // Assert +// assertNotNull(result); +// assertEquals("no of ecr details saved: 1", result); +// verify(recordRepo).findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong()); +// // getUserIdByName should still be called even for existing record due to service logic +// verify(userRepo).getUserIdByName("testUser"); // } // // @Test -// void testRegisterEligibleCoupleTracking_Success() { +// void registerEligibleCouple_exception_returnsError() { // // Arrange -// List eligibleCoupleTrackingDTOs = new ArrayList<>(); -// eligibleCoupleTrackingDTOs.add(new EligibleCoupleTrackingDTO()); +// EligibleCoupleDTO dto = new EligibleCoupleDTO(); +// dto.setBenId(1L); +// List dtoList = Arrays.asList(dto); +// +// when(eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(1L)) +// .thenThrow(new RuntimeException("Database error")); // // // Act -// String result = coupleService.registerEligibleCoupleTracking(eligibleCoupleTrackingDTOs); +// String result = coupleService.registerEligibleCouple(dtoList); // // // Assert // assertNotNull(result); -// assertEquals("saved successfully", result); +// assertTrue(result.startsWith("error while saving ecr details: ")); +// assertTrue(result.contains("Database error")); +// } +// +// @Test +// void registerEligibleCoupleTracking_newECTWithAntaraIncentive_success() { +// // Arrange +// EligibleCoupleTrackingDTO dto = new EligibleCoupleTrackingDTO(); +// dto.setBenId(1L); +// dto.setVisitDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setMethodOfContraception("ANTRA Injection"); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); +// +// EligibleCoupleTracking newECT = new EligibleCoupleTracking(); +// +// IncentiveActivity antaraActivity = new IncentiveActivity(); +// antaraActivity.setId(1L); +// antaraActivity.setName("ANTARA_PROG_1"); +// antaraActivity.setRate(500); +// +// when(eligibleCoupleTrackingRepo.findActiveEligibleCoupleTrackingByBenId(1L, dto.getVisitDate())) +// .thenReturn(null); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// 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())) +// .thenReturn(null); +// when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(Arrays.asList(newECT)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// doAnswer(invocation -> { +// EligibleCoupleTrackingDTO source = invocation.getArgument(0); +// EligibleCoupleTracking target = invocation.getArgument(1); +// target.setCreatedBy(source.getCreatedBy()); +// target.setMethodOfContraception(source.getMethodOfContraception()); +// target.setBenId(source.getBenId()); +// target.setCreatedDate(source.getCreatedDate()); +// return null; +// }).when(modelMapper).map(eq(dto), any(EligibleCoupleTracking.class)); +// +// // Act +// String result = coupleService.registerEligibleCoupleTracking(dtoList); // -// verify(eligibleCoupleTrackingRepo, times(1)).save(anyList()); +// // Assert +// assertNotNull(result); +// assertEquals("no of ect details saved: 1", result); +// verify(eligibleCoupleTrackingRepo).findActiveEligibleCoupleTrackingByBenId(1L, dto.getVisitDate()); +// 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(eligibleCoupleTrackingRepo).saveAll(anyList()); +// verify(recordRepo).saveAll(anyList()); // } // // @Test -// void testGetEligibleCoupleRegRecords_Success() { +// void registerEligibleCoupleTracking_existingECTWithAntaraProg2_success() { // // Arrange -// GetBenRequestHandler dto = new GetBenRequestHandler(); -// dto.setAshaId(123); -// dto.setFromDate(new Timestamp((new Date()).getTime())); -// dto.setToDate(new Timestamp((new Date()).getTime())); +// EligibleCoupleTrackingDTO dto = new EligibleCoupleTrackingDTO(); +// dto.setBenId(1L); +// dto.setVisitDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setMethodOfContraception("ANTRA Injection"); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); +// +// EligibleCoupleTracking existingECT = new EligibleCoupleTracking(); +// existingECT.setId(1L); // -// List eligibleCoupleRegisterList = new ArrayList<>(); -// eligibleCoupleRegisterList.add(new EligibleCoupleRegister()); +// List antaraHistory = Arrays.asList(new EligibleCoupleTracking()); // -// when(eligibleCoupleRegisterRepo.getECRegRecords(dto.getAshaId(), dto.getFromDate(), dto.getToDate())).thenReturn(eligibleCoupleRegisterList); +// IncentiveActivity antaraActivity2 = new IncentiveActivity(); +// antaraActivity2.setId(2L); +// antaraActivity2.setName("ANTARA_PROG_2"); +// antaraActivity2.setRate(600); +// +// when(eligibleCoupleTrackingRepo.findActiveEligibleCoupleTrackingByBenId(1L, dto.getVisitDate())) +// .thenReturn(existingECT); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleTrackingRepo.findCouplesHadAntara(1L)).thenReturn(antaraHistory); +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_2", "FAMILY PLANNING")) +// .thenReturn(antaraActivity2); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) +// .thenReturn(null); +// when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECT)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// doAnswer(invocation -> { +// EligibleCoupleTrackingDTO source = invocation.getArgument(0); +// EligibleCoupleTracking target = invocation.getArgument(1); +// target.setCreatedBy(source.getCreatedBy()); +// target.setMethodOfContraception(source.getMethodOfContraception()); +// target.setBenId(source.getBenId()); +// target.setCreatedDate(source.getCreatedDate()); +// return null; +// }).when(modelMapper).map(dto, existingECT); // // // Act -// List result = coupleService.getEligibleCoupleRegRecords(dto); +// String result = coupleService.registerEligibleCoupleTracking(dtoList); // // // Assert // assertNotNull(result); -// assertEquals(eligibleCoupleRegisterList.size(), result.size()); +// assertEquals("no of ect details saved: 1", result); +// verify(incentivesRepo).findIncentiveMasterByNameAndGroup("ANTARA_PROG_2", "FAMILY PLANNING"); +// verify(modelMapper).map(dto, existingECT); +// } +// +// @Test +// void registerEligibleCoupleTracking_antaraProg3And4_success() { +// // Test for ANTARA_PROG_3 (numAntaraDosage = 2) +// EligibleCoupleTrackingDTO dto1 = new EligibleCoupleTrackingDTO(); +// dto1.setBenId(1L); +// dto1.setVisitDate(Timestamp.valueOf(LocalDateTime.now())); +// dto1.setMethodOfContraception("ANTRA Injection"); +// dto1.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto1.setCreatedBy("testUser"); +// +// List antaraHistory3 = Arrays.asList( +// new EligibleCoupleTracking(), new EligibleCoupleTracking() +// ); +// +// IncentiveActivity antaraActivity3 = new IncentiveActivity(); +// antaraActivity3.setId(3L); +// antaraActivity3.setName("ANTARA_PROG_3"); +// antaraActivity3.setRate(700); // -// verify(eligibleCoupleRegisterRepo, times(1)).getECRegRecords(dto.getAshaId(), dto.getFromDate(), dto.getToDate()); +// when(eligibleCoupleTrackingRepo.findActiveEligibleCoupleTrackingByBenId(1L, dto1.getVisitDate())) +// .thenReturn(null); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleTrackingRepo.findCouplesHadAntara(1L)).thenReturn(antaraHistory3); +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_3", "FAMILY PLANNING")) +// .thenReturn(antaraActivity3); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) +// .thenReturn(null); +// when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// doAnswer(invocation -> { +// EligibleCoupleTrackingDTO source = invocation.getArgument(0); +// EligibleCoupleTracking target = invocation.getArgument(1); +// target.setCreatedBy(source.getCreatedBy()); +// target.setMethodOfContraception(source.getMethodOfContraception()); +// target.setBenId(source.getBenId()); +// target.setCreatedDate(source.getCreatedDate()); +// return null; +// }).when(modelMapper).map(eq(dto1), any(EligibleCoupleTracking.class)); +// +// String result1 = coupleService.registerEligibleCoupleTracking(Arrays.asList(dto1)); +// assertNotNull(result1); +// verify(incentivesRepo).findIncentiveMasterByNameAndGroup("ANTARA_PROG_3", "FAMILY PLANNING"); +// +// // Test for ANTARA_PROG_4 (numAntaraDosage = 3) +// reset(eligibleCoupleTrackingRepo); +// reset(incentivesRepo); +// reset(recordRepo); +// reset(userRepo); +// +// EligibleCoupleTrackingDTO dto2 = new EligibleCoupleTrackingDTO(); +// dto2.setBenId(2L); +// dto2.setVisitDate(Timestamp.valueOf(LocalDateTime.now())); +// dto2.setMethodOfContraception("ANTRA Injection"); +// dto2.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto2.setCreatedBy("testUser"); +// +// List antaraHistory4 = Arrays.asList( +// new EligibleCoupleTracking(), new EligibleCoupleTracking(), new EligibleCoupleTracking() +// ); +// +// IncentiveActivity antaraActivity4 = new IncentiveActivity(); +// antaraActivity4.setId(4L); +// antaraActivity4.setName("ANTARA_PROG_4"); +// antaraActivity4.setRate(800); +// +// when(eligibleCoupleTrackingRepo.findActiveEligibleCoupleTrackingByBenId(2L, dto2.getVisitDate())) +// .thenReturn(null); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleTrackingRepo.findCouplesHadAntara(2L)).thenReturn(antaraHistory4); +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_4", "FAMILY PLANNING")) +// .thenReturn(antaraActivity4); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(anyLong(), any(Timestamp.class), anyLong())) +// .thenReturn(null); +// when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// doAnswer(invocation -> { +// EligibleCoupleTrackingDTO source = invocation.getArgument(0); +// EligibleCoupleTracking target = invocation.getArgument(1); +// target.setCreatedBy(source.getCreatedBy()); +// target.setMethodOfContraception(source.getMethodOfContraception()); +// target.setBenId(source.getBenId()); +// target.setCreatedDate(source.getCreatedDate()); +// return null; +// }).when(modelMapper).map(eq(dto2), any(EligibleCoupleTracking.class)); +// +// String result2 = coupleService.registerEligibleCoupleTracking(Arrays.asList(dto2)); +// assertNotNull(result2); +// verify(incentivesRepo).findIncentiveMasterByNameAndGroup("ANTARA_PROG_4", "FAMILY PLANNING"); // } // // @Test -// void testGetEligibleCoupleTracking_Success() { +// void registerEligibleCoupleTracking_noIncentiveForNonAntara_success() { // // Arrange -// GetBenRequestHandler dto = new GetBenRequestHandler(); -// dto.setAshaId(123); -// dto.setFromDate(new Timestamp((new Date()).getTime())); -// dto.setToDate(new Timestamp((new Date()).getTime())); +// EligibleCoupleTrackingDTO dto = new EligibleCoupleTrackingDTO(); +// dto.setBenId(1L); +// dto.setVisitDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setMethodOfContraception("Other method"); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); +// +// when(eligibleCoupleTrackingRepo.findActiveEligibleCoupleTrackingByBenId(1L, dto.getVisitDate())) +// .thenReturn(null); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleTrackingRepo.findCouplesHadAntara(1L)).thenReturn(new ArrayList<>()); +// when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// doAnswer(invocation -> { +// EligibleCoupleTrackingDTO source = invocation.getArgument(0); +// EligibleCoupleTracking target = invocation.getArgument(1); +// target.setCreatedBy(source.getCreatedBy()); +// target.setMethodOfContraception(source.getMethodOfContraception()); +// target.setBenId(source.getBenId()); +// target.setCreatedDate(source.getCreatedDate()); +// return null; +// }).when(modelMapper).map(eq(dto), any(EligibleCoupleTracking.class)); +// +// // Act +// String result = coupleService.registerEligibleCoupleTracking(dtoList); // -// List eligibleCoupleTrackingList = new ArrayList<>(); -// eligibleCoupleTrackingList.add(new EligibleCoupleTracking()); +// // Assert +// assertNotNull(result); +// assertEquals("no of ect details saved: 1", result); +// verify(incentivesRepo, never()).findIncentiveMasterByNameAndGroup(anyString(), anyString()); +// } // -// when(eligibleCoupleTrackingRepo.getECTrackRecords(dto.getAshaId(), dto.getFromDate(), dto.getToDate())).thenReturn(eligibleCoupleTrackingList); +// @Test +// void registerEligibleCoupleTracking_exception_returnsError() { +// // Arrange +// EligibleCoupleTrackingDTO dto = new EligibleCoupleTrackingDTO(); +// dto.setBenId(1L); +// List dtoList = Arrays.asList(dto); +// +// when(eligibleCoupleTrackingRepo.findActiveEligibleCoupleTrackingByBenId(anyLong(), any())) +// .thenThrow(new RuntimeException("Database error")); +// +// // Act +// String result = coupleService.registerEligibleCoupleTracking(dtoList); +// +// // Assert +// assertNotNull(result); +// assertTrue(result.startsWith("error while saving ect details: ")); +// assertTrue(result.contains("Database error")); +// } +// +// @Test +// void getEligibleCoupleRegRecords_success() { +// // Arrange +// GetBenRequestHandler dto = mock(GetBenRequestHandler.class); +// Integer ashaId = 123; +// Timestamp fromDate = Timestamp.valueOf(LocalDateTime.now().minusDays(30)); +// Timestamp toDate = Timestamp.valueOf(LocalDateTime.now()); +// +// when(dto.getAshaId()).thenReturn(ashaId); +// when(dto.getFromDate()).thenReturn(fromDate); +// when(dto.getToDate()).thenReturn(toDate); +// +// String userName = "testUser"; +// List registerList = Arrays.asList(new EligibleCoupleRegister()); +// EligibleCoupleDTO mappedDTO = new EligibleCoupleDTO(); +// +// when(beneficiaryRepo.getUserName(ashaId)).thenReturn(userName); +// when(eligibleCoupleRegisterRepo.getECRegRecords(userName, fromDate, toDate)).thenReturn(registerList); +// when(mapper.convertValue(any(EligibleCoupleRegister.class), eq(EligibleCoupleDTO.class))) +// .thenReturn(mappedDTO); +// +// // Act +// String result = coupleService.getEligibleCoupleRegRecords(dto); +// +// // Assert +// assertNotNull(result); +// @SuppressWarnings("unchecked") +// List resultList = new Gson().fromJson(result, List.class); +// assertEquals(1, resultList.size()); +// +// verify(beneficiaryRepo).getUserName(ashaId); +// verify(eligibleCoupleRegisterRepo).getECRegRecords(userName, fromDate, toDate); +// verify(mapper).convertValue(any(EligibleCoupleRegister.class), eq(EligibleCoupleDTO.class)); +// } +// +// @Test +// void getEligibleCoupleRegRecords_exception_returnsNull() { +// // Arrange +// GetBenRequestHandler dto = mock(GetBenRequestHandler.class); +// when(dto.getAshaId()).thenReturn(123); +// when(beneficiaryRepo.getUserName(123)).thenThrow(new RuntimeException("Database error")); +// +// // Act +// String result = coupleService.getEligibleCoupleRegRecords(dto); +// +// // Assert +// assertNull(result); +// verify(beneficiaryRepo).getUserName(123); +// } +// +// @Test +// void getEligibleCoupleTracking_success() { +// // Arrange +// GetBenRequestHandler dto = mock(GetBenRequestHandler.class); +// Integer ashaId = 123; +// Timestamp fromDate = Timestamp.valueOf(LocalDateTime.now().minusDays(30)); +// Timestamp toDate = Timestamp.valueOf(LocalDateTime.now()); +// +// when(dto.getAshaId()).thenReturn(ashaId); +// when(dto.getFromDate()).thenReturn(fromDate); +// when(dto.getToDate()).thenReturn(toDate); +// +// String userName = "testUser"; +// List trackingList = Arrays.asList(new EligibleCoupleTracking()); +// EligibleCoupleTrackingDTO mappedDTO = new EligibleCoupleTrackingDTO(); +// +// when(beneficiaryRepo.getUserName(ashaId)).thenReturn(userName); +// when(eligibleCoupleTrackingRepo.getECTrackRecords(userName, fromDate, toDate)).thenReturn(trackingList); +// when(mapper.convertValue(any(EligibleCoupleTracking.class), eq(EligibleCoupleTrackingDTO.class))) +// .thenReturn(mappedDTO); +// +// // Act +// List result = coupleService.getEligibleCoupleTracking(dto); +// +// // Assert +// assertNotNull(result); +// assertEquals(1, result.size()); +// assertEquals(mappedDTO, result.get(0)); +// +// verify(beneficiaryRepo).getUserName(ashaId); +// verify(eligibleCoupleTrackingRepo).getECTrackRecords(userName, fromDate, toDate); +// verify(mapper).convertValue(any(EligibleCoupleTracking.class), eq(EligibleCoupleTrackingDTO.class)); +// } +// +// @Test +// void getEligibleCoupleTracking_exception_returnsNull() { +// // Arrange +// GetBenRequestHandler dto = mock(GetBenRequestHandler.class); +// when(dto.getAshaId()).thenReturn(123); +// when(beneficiaryRepo.getUserName(123)).thenThrow(new RuntimeException("Database error")); // // // Act // List result = coupleService.getEligibleCoupleTracking(dto); // // // Assert +// assertNull(result); +// verify(beneficiaryRepo).getUserName(123); +// } +// +// @Test +// void registerEligibleCoupleTracking_nullIncentiveActivity_noRecordAdded() { +// // Arrange +// EligibleCoupleTrackingDTO dto = new EligibleCoupleTrackingDTO(); +// dto.setBenId(1L); +// dto.setVisitDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setMethodOfContraception("ANTRA Injection"); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); +// +// when(eligibleCoupleTrackingRepo.findActiveEligibleCoupleTrackingByBenId(1L, dto.getVisitDate())) +// .thenReturn(null); +// when(userRepo.getUserIdByName("testUser")).thenReturn(123); +// when(eligibleCoupleTrackingRepo.findCouplesHadAntara(1L)).thenReturn(new ArrayList<>()); +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANTARA_PROG_1", "FAMILY PLANNING")) +// .thenReturn(null); +// when(eligibleCoupleTrackingRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// doAnswer(invocation -> { +// EligibleCoupleTrackingDTO source = invocation.getArgument(0); +// EligibleCoupleTracking target = invocation.getArgument(1); +// target.setCreatedBy(source.getCreatedBy()); +// target.setMethodOfContraception(source.getMethodOfContraception()); +// target.setBenId(source.getBenId()); +// target.setCreatedDate(source.getCreatedDate()); +// return null; +// }).when(modelMapper).map(eq(dto), any(EligibleCoupleTracking.class)); +// +// // Act +// String result = coupleService.registerEligibleCoupleTracking(dtoList); +// +// // Assert // assertNotNull(result); -// assertEquals(eligibleCoupleTrackingList.size(), result.size()); +// 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(eligibleCoupleTrackingRepo, times(1)).getECTrackRecords(dto.getAshaId(), dto.getFromDate(), dto.getToDate()); +// @Test +// void registerEligibleCouple_nullIncentiveActivity_noRecordCreated() { +// // Arrange +// EligibleCoupleDTO dto = new EligibleCoupleDTO(); +// dto.setBenId(1L); +// dto.setNumLiveChildren(1); +// dto.setMarriageFirstChildGap(3); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); +// +// EligibleCoupleRegister existingECR = new EligibleCoupleRegister(); +// existingECR.setId(1L); +// existingECR.setNumLiveChildren(0); +// +// when(eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(1L)).thenReturn(existingECR); +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("MARRIAGE_1st_CHILD_GAP", "FAMILY PLANNING")) +// .thenReturn(null); +// when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// +// // Act +// String result = coupleService.registerEligibleCouple(dtoList); +// +// // Assert +// 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()); +// } +// +// @Test +// void registerEligibleCouple_existingECRNullNumLiveChildren_newRecord() { +// // Arrange +// EligibleCoupleDTO dto = new EligibleCoupleDTO(); +// dto.setBenId(1L); +// dto.setNumLiveChildren(1); +// dto.setCreatedDate(Timestamp.valueOf(LocalDateTime.now())); +// dto.setCreatedBy("testUser"); +// List dtoList = Arrays.asList(dto); +// +// EligibleCoupleRegister existingECR = new EligibleCoupleRegister(); +// existingECR.setId(1L); +// existingECR.setNumLiveChildren(null); // null numLiveChildren should trigger new record creation +// +// when(eligibleCoupleRegisterRepo.findEligibleCoupleRegisterByBenId(1L)).thenReturn(existingECR); +// when(eligibleCoupleRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingECR)); +// when(recordRepo.saveAll(anyList())).thenReturn(new ArrayList<>()); +// +// // Act +// String result = coupleService.registerEligibleCouple(dtoList); +// +// // Assert +// assertNotNull(result); +// assertEquals("no of ecr details saved: 1", result); +// // Should not call incentive methods because numLiveChildren is null +// verify(incentivesRepo, never()).findIncentiveMasterByNameAndGroup(anyString(), anyString()); +// verify(modelMapper).map(eq(dto), any(EligibleCoupleRegister.class)); // } -} - - +//} diff --git a/src/test/java/com/iemr/flw/service/impl/DeathReportsServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/DeathReportsServiceImplTest.java new file mode 100644 index 000000000..97cbc3b2b --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/DeathReportsServiceImplTest.java @@ -0,0 +1,164 @@ +package com.iemr.flw.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iemr.flw.domain.iemr.CDR; +import com.iemr.flw.domain.iemr.MDSR; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.CdrDTO; +import com.iemr.flw.dto.iemr.MdsrDTO; +import com.iemr.flw.repo.identity.BeneficiaryRepo; +import com.iemr.flw.repo.iemr.CdrRepo; +import com.iemr.flw.repo.iemr.MdsrRepo; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; + +import java.sql.Timestamp; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class DeathReportsServiceImplTest { + @Mock + private CdrRepo cdrRepo; + @Mock + private MdsrRepo mdsrRepo; + @Mock + private BeneficiaryRepo beneficiaryRepo; + @Mock + private ObjectMapper mapper; + @Mock + private ModelMapper modelMapper; + @InjectMocks + private DeathReportsServiceImpl deathReportsService; + + @Test + void registerCDR_new_success() { + CdrDTO dto = new CdrDTO(); + dto.setBenId(1L); + List dtos = Collections.singletonList(dto); + when(cdrRepo.findCDRByBenId(1L)).thenReturn(null); + doNothing().when(modelMapper).map(any(CdrDTO.class), any(CDR.class)); + String result = deathReportsService.registerCDR(dtos); + assertTrue(result.contains("no of cdr details saved: 1")); + } + + @Test + void registerCDR_existing_success() { + CdrDTO dto = new CdrDTO(); + dto.setBenId(2L); + List dtos = Collections.singletonList(dto); + CDR existing = new CDR(); + existing.setId(99L); + when(cdrRepo.findCDRByBenId(2L)).thenReturn(existing); + doNothing().when(modelMapper).map(any(CdrDTO.class), any(CDR.class)); + String result = deathReportsService.registerCDR(dtos); + assertTrue(result.contains("no of cdr details saved: 1")); + } + + @Test + void registerCDR_exception() { + CdrDTO dto = new CdrDTO(); + dto.setBenId(3L); + List dtos = Collections.singletonList(dto); + when(cdrRepo.findCDRByBenId(3L)).thenThrow(new RuntimeException("fail")); + String result = deathReportsService.registerCDR(dtos); + assertTrue(result.startsWith("error while saving cdr details:")); + } + + @Test + void registerMDSR_new_success() { + MdsrDTO dto = new MdsrDTO(); + dto.setBenId(1L); + List dtos = Collections.singletonList(dto); + when(mdsrRepo.findMDSRByBenId(1L)).thenReturn(null); + doNothing().when(modelMapper).map(any(MdsrDTO.class), any(MDSR.class)); + String result = deathReportsService.registerMDSR(dtos); + assertTrue(result.contains("no of mdsr details saved: 1")); + } + + @Test + void registerMDSR_existing_success() { + MdsrDTO dto = new MdsrDTO(); + dto.setBenId(2L); + List dtos = Collections.singletonList(dto); + MDSR existing = new MDSR(); + existing.setId(99L); + when(mdsrRepo.findMDSRByBenId(2L)).thenReturn(existing); + doNothing().when(modelMapper).map(any(MdsrDTO.class), any(MDSR.class)); + String result = deathReportsService.registerMDSR(dtos); + assertTrue(result.contains("no of mdsr details saved: 1")); + } + + @Test + void registerMDSR_exception() { + MdsrDTO dto = new MdsrDTO(); + dto.setBenId(3L); + List dtos = Collections.singletonList(dto); + when(mdsrRepo.findMDSRByBenId(3L)).thenThrow(new RuntimeException("fail")); + String result = deathReportsService.registerMDSR(dtos); + assertTrue(result.startsWith("error while saving mdsr details:")); + } + + @Test + 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"); + List cdrList = Arrays.asList(new CDR(), new CDR()); +// when(cdrRepo.getAllCdrByBenId("user1", fromDate, toDate)).thenReturn(cdrList); + when(mapper.convertValue(any(CDR.class), eq(CdrDTO.class))).thenReturn(new CdrDTO()); + List result = deathReportsService.getCdrRecords(dto); + assertNotNull(result); + assertEquals(2, result.size()); + } + + @Test + void getCdrRecords_exception() { + GetBenRequestHandler dto = mock(GetBenRequestHandler.class); + Integer ashaId = 123; + when(dto.getAshaId()).thenReturn(ashaId); + when(beneficiaryRepo.getUserName(ashaId)).thenThrow(new RuntimeException("fail")); + List result = deathReportsService.getCdrRecords(dto); + assertNull(result); + } + + @Test + 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"); + List mdsrList = Arrays.asList(new MDSR(), new MDSR()); +// when(mdsrRepo.getAllMdsrByAshaId("user1", fromDate, toDate)).thenReturn(mdsrList); + when(mapper.convertValue(any(MDSR.class), eq(MdsrDTO.class))).thenReturn(new MdsrDTO()); + List result = deathReportsService.getMdsrRecords(dto); + assertNotNull(result); + assertEquals(2, result.size()); + } + + @Test + void getMdsrRecords_exception() { + GetBenRequestHandler dto = mock(GetBenRequestHandler.class); + Integer ashaId = 123; + when(dto.getAshaId()).thenReturn(ashaId); + when(beneficiaryRepo.getUserName(ashaId)).thenThrow(new RuntimeException("fail")); + List result = deathReportsService.getMdsrRecords(dto); + assertNull(result); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImplTest.java index 8cc14ab08..c70c92e57 100644 --- a/src/test/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/DeliveryOutcomeServiceImplTest.java @@ -1,75 +1,91 @@ package com.iemr.flw.service.impl; -import com.fasterxml.jackson.databind.ObjectMapper; import com.iemr.flw.domain.iemr.DeliveryOutcome; import com.iemr.flw.dto.identity.GetBenRequestHandler; import com.iemr.flw.dto.iemr.DeliveryOutcomeDTO; +import com.iemr.flw.repo.identity.BeneficiaryRepo; import com.iemr.flw.repo.iemr.DeliveryOutcomeRepo; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; - import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Date; +import java.util.Arrays; +import java.util.Collections; import java.util.List; - import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; class DeliveryOutcomeServiceImplTest { - @Mock private DeliveryOutcomeRepo deliveryOutcomeRepo; + @Mock + private BeneficiaryRepo beneficiaryRepo; @InjectMocks - private DeliveryOutcomeServiceImpl deliveryOutcomeService; - - private ObjectMapper mapper = new ObjectMapper(); + private DeliveryOutcomeServiceImpl service; @BeforeEach void setUp() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); + } + + @Test + void registerDeliveryOutcome_newAndExisting() { + DeliveryOutcomeDTO dto1 = new DeliveryOutcomeDTO(); + dto1.setBenId(1L); + DeliveryOutcomeDTO dto2 = new DeliveryOutcomeDTO(); + dto2.setBenId(2L); + List dtos = Arrays.asList(dto1, dto2); + DeliveryOutcome existing = new DeliveryOutcome(); + existing.setId(99L); + when(deliveryOutcomeRepo.findDeliveryOutcomeByBenIdAndIsActive(1L, true)).thenReturn(null); + when(deliveryOutcomeRepo.findDeliveryOutcomeByBenIdAndIsActive(2L, true)).thenReturn(existing); + String result = service.registerDeliveryOutcome(dtos); + assertTrue(result.contains("no of delivery outcome details saved: 2")); + verify(deliveryOutcomeRepo).saveAll(anyList()); + } + + @Test + void registerDeliveryOutcome_exception() { + DeliveryOutcomeDTO dto = new DeliveryOutcomeDTO(); + dto.setBenId(1L); + List dtos = Collections.singletonList(dto); + when(deliveryOutcomeRepo.findDeliveryOutcomeByBenIdAndIsActive(1L, true)).thenReturn(null); + when(deliveryOutcomeRepo.saveAll(anyList())).thenThrow(new RuntimeException("Database error")); + String result = service.registerDeliveryOutcome(dtos); + assertTrue(result.startsWith("error while saving delivery outcome details:")); } -// @Test -// void testRegisterDeliveryOutcome_Success() { -// // Arrange -// List deliveryOutcomeDTOs = new ArrayList<>(); -// deliveryOutcomeDTOs.add(new DeliveryOutcomeDTO()); -// -// // Act -// String result = deliveryOutcomeService.registerDeliveryOutcome(deliveryOutcomeDTOs); -// -// // Assert -// assertNotNull(result); -// assertEquals("saved successfully", result); -// -// verify(deliveryOutcomeRepo, times(1)).save(anyList()); -// } -// -// @Test -// void testGetDeliveryOutcome_Success() { -// // Arrange -// GetBenRequestHandler dto = new GetBenRequestHandler(); -// dto.setAshaId(123); -// dto.setFromDate(new Timestamp((new Date()).getTime())); -// dto.setToDate(new Timestamp((new Date()).getTime())); -// -// List deliveryOutcomeList = new ArrayList<>(); -// deliveryOutcomeList.add(new DeliveryOutcome()); -// -// when(deliveryOutcomeRepo.getDeliveryOutcomeByBenId(dto.getAshaId(), dto.getFromDate(), dto.getToDate())).thenReturn(deliveryOutcomeList); -// -// // Act -// List result = deliveryOutcomeService.getDeliveryOutcome(dto); -// -// // Assert -// assertNotNull(result); -// assertEquals(deliveryOutcomeList.size(), result.size()); -// -// verify(deliveryOutcomeRepo, times(1)).getDeliveryOutcomeByBenId(dto.getAshaId(), dto.getFromDate(), dto.getToDate()); -// } + @Test + void getDeliveryOutcome_success() { + GetBenRequestHandler req = new GetBenRequestHandler(); + req.setAshaId(123); + Timestamp from = Timestamp.valueOf("2023-01-01 00:00:00"); + Timestamp to = Timestamp.valueOf("2023-12-31 00:00:00"); + req.setFromDate(from); + req.setToDate(to); + when(beneficiaryRepo.getUserName(123)).thenReturn("user"); + DeliveryOutcome outcome = new DeliveryOutcome(); + when(deliveryOutcomeRepo.getDeliveryOutcomeByAshaId("user", from, to)).thenReturn(Collections.singletonList(outcome)); + List result = service.getDeliveryOutcome(req); + assertNotNull(result); + assertEquals(1, result.size()); + } + + @Test + void getDeliveryOutcome_exception() { + GetBenRequestHandler req = new GetBenRequestHandler(); + req.setAshaId(123); + Timestamp from = Timestamp.valueOf("2023-01-01 00:00:00"); + Timestamp to = Timestamp.valueOf("2023-12-31 00:00:00"); + req.setFromDate(from); + req.setToDate(to); + when(beneficiaryRepo.getUserName(123)).thenReturn("user"); + when(deliveryOutcomeRepo.getDeliveryOutcomeByAshaId(anyString(), any(Timestamp.class), any(Timestamp.class))).thenThrow(new RuntimeException("fail")); + List result = service.getDeliveryOutcome(req); + assertNull(result); + } } diff --git a/src/test/java/com/iemr/flw/service/impl/HRNonPregnantServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/HRNonPregnantServiceImplTest.java new file mode 100644 index 000000000..d17f2bbf4 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/HRNonPregnantServiceImplTest.java @@ -0,0 +1,197 @@ +package com.iemr.flw.service.impl; + +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.NonPregnantWomanHighRiskAssess; +import com.iemr.flw.domain.iemr.NonPregnantWomanHighRiskTrack; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.HRNonPregnantAssessDTO; +import com.iemr.flw.dto.iemr.HRNonPregnantTrackDTO; +import com.iemr.flw.dto.iemr.HRPregnantAssessDTO; +import com.iemr.flw.dto.iemr.UserDataDTO; +import com.iemr.flw.repo.iemr.HRNonPregnantAssessRepo; +import com.iemr.flw.repo.iemr.HRNonPregnantTrackRepo; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; + +import java.sql.Timestamp; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class HRNonPregnantServiceImplTest { + @Mock + private HRNonPregnantAssessRepo assessRepo; + @Mock + private HRNonPregnantTrackRepo trackRepo; + @Mock + private ModelMapper modelMapper; + @InjectMocks + private HRNonPregnantServiceImpl service; + + @Test + void getAllAssessment_success() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + List entityList = Arrays.asList(new NonPregnantWomanHighRiskAssess(), new NonPregnantWomanHighRiskAssess()); + when(assessRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(entityList); + String json = service.getAllAssessment(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(2, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void getAllAssessment_emptyList() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + when(assessRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); + String json = service.getAllAssessment(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(0, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void saveAllAssessment_newAssess_success() { + HRNonPregnantAssessDTO dto = new HRNonPregnantAssessDTO(); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(10); + req.setEntries(Collections.singletonList(dto)); + HRPregnantAssessDTO mapped = new HRPregnantAssessDTO(); + mapped.setBenId(1L); + when(assessRepo.getByUserIdAndBenId(isNull(), eq(10))).thenReturn(null); + // No need to stub assessRepo.save for void/irrelevant methods + String result = service.saveAllAssessment(req); + assertTrue(result.contains("no of high risk assessment items saved: 1")); + } + + @Test + void saveAllAssessment_existingAssess_success() { + HRNonPregnantAssessDTO dto = new HRNonPregnantAssessDTO(); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(20); + req.setEntries(Collections.singletonList(dto)); + HRPregnantAssessDTO mapped = new HRPregnantAssessDTO(); + mapped.setBenId(2L); + NonPregnantWomanHighRiskAssess existing = new NonPregnantWomanHighRiskAssess(); + existing.setId(99L); + + when(assessRepo.getByUserIdAndBenId(isNull(), eq(20))).thenReturn(existing); + // No need to stub assessRepo.save for void/irrelevant methods + String result = service.saveAllAssessment(req); + assertTrue(result.contains("no of high risk assessment items saved: 1")); + } + + @Test + void saveAllAssessment_exception() { + HRNonPregnantAssessDTO dto = new HRNonPregnantAssessDTO(); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(30); + req.setEntries(Collections.singletonList(dto)); + HRPregnantAssessDTO mapped = new HRPregnantAssessDTO(); + mapped.setBenId(3L); + when(modelMapper.map(dto, HRPregnantAssessDTO.class)).thenReturn(mapped); + when(assessRepo.getByUserIdAndBenId(3L, 30)).thenThrow(new RuntimeException("fail")); + assertThrows(RuntimeException.class, () -> service.saveAllAssessment(req)); + } + + @Test + void getAllTracking_success() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + List entityList = Arrays.asList(new NonPregnantWomanHighRiskTrack(), new NonPregnantWomanHighRiskTrack()); + when(trackRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(entityList); + String json = service.getAllTracking(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(2, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void getAllTracking_emptyList() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + when(trackRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); + String json = service.getAllTracking(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(0, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void saveAllTracking_newTrack_success() { + HRNonPregnantTrackDTO dto = new HRNonPregnantTrackDTO(); + dto.setBenId(1L); + dto.setVisitDate(new java.sql.Timestamp(System.currentTimeMillis())); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(10); + req.setEntries(Collections.singletonList(dto)); + when(trackRepo.getByUserIdAndBenId(dto.getBenId(), 10, dto.getVisitDate())).thenReturn(null); + when(trackRepo.save(any(NonPregnantWomanHighRiskTrack.class))).thenReturn(new NonPregnantWomanHighRiskTrack()); + String result = service.saveAllTracking(req); + assertTrue(result.contains("no of high risk pregnant tracking items saved: 1")); + } + + @Test + void saveAllTracking_existingTrack_success() { + HRNonPregnantTrackDTO dto = new HRNonPregnantTrackDTO(); + dto.setBenId(2L); + dto.setVisitDate(new java.sql.Timestamp(System.currentTimeMillis())); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(20); + req.setEntries(Collections.singletonList(dto)); + NonPregnantWomanHighRiskTrack existing = new NonPregnantWomanHighRiskTrack(); + existing.setId(99L); + when(trackRepo.getByUserIdAndBenId(dto.getBenId(), 20, dto.getVisitDate())).thenReturn(existing); + + when(trackRepo.save(any(NonPregnantWomanHighRiskTrack.class))).thenReturn(existing); + String result = service.saveAllTracking(req); + assertTrue(result.contains("no of high risk pregnant tracking items saved: 1")); + } + + @Test + void saveAllTracking_exception() { + HRNonPregnantTrackDTO dto = new HRNonPregnantTrackDTO(); + dto.setBenId(3L); + dto.setVisitDate(new java.sql.Timestamp(System.currentTimeMillis())); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(30); + req.setEntries(Collections.singletonList(dto)); + // No need to stub assessRepo.getByUserIdAndBenId for this test + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/HRPregnantServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/HRPregnantServiceImplTest.java new file mode 100644 index 000000000..08a4fe35c --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/HRPregnantServiceImplTest.java @@ -0,0 +1,190 @@ +package com.iemr.flw.service.impl; + +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.PregnantWomanHighRiskAssess; +import com.iemr.flw.domain.iemr.PregnantWomanHighRiskTrack; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.HRPregnantAssessDTO; +import com.iemr.flw.dto.iemr.HRPregnantTrackDTO; +import com.iemr.flw.dto.iemr.UserDataDTO; +import com.iemr.flw.repo.iemr.HRPregnantAssessRepo; +import com.iemr.flw.repo.iemr.HRPregnantTrackRepo; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; + +import java.sql.Timestamp; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class HRPregnantServiceImplTest { + @Mock + private HRPregnantAssessRepo assessRepo; + @Mock + private HRPregnantTrackRepo trackRepo; + @Mock + private ModelMapper modelMapper; + @InjectMocks + private HRPregnantServiceImpl service; + + @Test + void getAllAssessments_success() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + List entityList = Arrays.asList(new PregnantWomanHighRiskAssess(), new PregnantWomanHighRiskAssess()); + when(assessRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(entityList); + String json = service.getAllAssessments(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(2, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void getAllAssessments_emptyList() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + when(assessRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); + String json = service.getAllAssessments(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(0, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void saveAllAssessment_newAssess_success() { + HRPregnantAssessDTO dto = new HRPregnantAssessDTO(); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(10); + req.setEntries(Collections.singletonList(dto)); + HRPregnantAssessDTO mapped = new HRPregnantAssessDTO(); + mapped.setBenId(1L); + when(assessRepo.getByUserIdAndBenId(isNull(), eq(10))).thenReturn(null); + String result = service.saveAllAssessment(req); + assertTrue(result.contains("no of high risk assessment items saved: 1")); + } + + @Test + void saveAllAssessment_existingAssess_success() { + HRPregnantAssessDTO dto = new HRPregnantAssessDTO(); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(20); + req.setEntries(Collections.singletonList(dto)); + HRPregnantAssessDTO mapped = new HRPregnantAssessDTO(); + mapped.setBenId(2L); + PregnantWomanHighRiskAssess existing = new PregnantWomanHighRiskAssess(); + existing.setId(99L); + when(assessRepo.getByUserIdAndBenId(isNull(), eq(20))).thenReturn(existing); + String result = service.saveAllAssessment(req); + assertTrue(result.contains("no of high risk assessment items saved: 1")); + } + + @Test + void saveAllAssessment_exception() { + HRPregnantAssessDTO dto = new HRPregnantAssessDTO(); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(30); + req.setEntries(Collections.singletonList(dto)); + HRPregnantAssessDTO mapped = new HRPregnantAssessDTO(); + mapped.setBenId(3L); + when(assessRepo.getByUserIdAndBenId(isNull(), eq(30))).thenThrow(new RuntimeException("fail")); + assertThrows(RuntimeException.class, () -> service.saveAllAssessment(req)); + } + + @Test + void getAllTracking_success() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + List entityList = Arrays.asList(new PregnantWomanHighRiskTrack(), new PregnantWomanHighRiskTrack()); + when(trackRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(entityList); + String json = service.getAllTracking(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(2, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void getAllTracking_emptyList() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + when(trackRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); + String json = service.getAllTracking(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(0, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void saveAllTracking_newTrack_success() { + HRPregnantTrackDTO dto = new HRPregnantTrackDTO(); + dto.setBenId(1L); + dto.setVisit("2023-08-16"); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(10); + req.setEntries(Collections.singletonList(dto)); + when(trackRepo.getByUserIdAndBenId(dto.getBenId(), 10, dto.getVisit())).thenReturn(null); + String result = service.saveAllTracking(req); + assertTrue(result.contains("no of high risk pregnant tracking items saved: 1")); + } + + @Test + void saveAllTracking_existingTrack_success() { + HRPregnantTrackDTO dto = new HRPregnantTrackDTO(); + dto.setBenId(2L); + dto.setVisit("2023-08-16"); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(20); + req.setEntries(Collections.singletonList(dto)); + PregnantWomanHighRiskTrack existing = new PregnantWomanHighRiskTrack(); + existing.setId(99L); + when(trackRepo.getByUserIdAndBenId(dto.getBenId(), 20, dto.getVisit())).thenReturn(existing); + String result = service.saveAllTracking(req); + assertTrue(result.contains("no of high risk pregnant tracking items saved: 1")); + } + + @Test + void saveAllTracking_exception() { + HRPregnantTrackDTO dto = new HRPregnantTrackDTO(); + dto.setBenId(3L); + dto.setVisit("2023-08-16"); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(30); + req.setEntries(Collections.singletonList(dto)); + when(trackRepo.getByUserIdAndBenId(dto.getBenId(), 30, dto.getVisit())).thenThrow(new RuntimeException("fail")); + assertThrows(RuntimeException.class, () -> service.saveAllTracking(req)); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/HighRiskServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/HighRiskServiceImplTest.java new file mode 100644 index 000000000..e440d6206 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/HighRiskServiceImplTest.java @@ -0,0 +1,114 @@ +package com.iemr.flw.service.impl; + +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.HighRiskAssess; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.HighRiskAssessDTO; +import com.iemr.flw.dto.iemr.UserDataDTO; +import com.iemr.flw.repo.iemr.HighRiskAssessRepo; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; + +import java.sql.Timestamp; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class HighRiskServiceImplTest { + @Mock + private HighRiskAssessRepo assessRepo; + @Mock + private ModelMapper modelMapper; + @InjectMocks + private HighRiskServiceImpl highRiskService; + + @Test + void getAllAssessments_success() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + List entityList = Arrays.asList(new HighRiskAssess(), new HighRiskAssess()); + when(assessRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(entityList); + String json = highRiskService.getAllAssessments(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(2, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void getAllAssessments_emptyList() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + when(assessRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); + String json = highRiskService.getAllAssessments(req); + assertNotNull(json); + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>(){}.getType(); + UserDataDTO result = new Gson().fromJson(json, type); + assertEquals(0, result.getEntries().size()); + assertEquals(ashaId, result.getUserId()); + } + + @Test + void saveAllAssessment_newAssess_success() { + HighRiskAssessDTO dto = new HighRiskAssessDTO(); + dto.setBenId(1L); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(10); + req.setEntries(Collections.singletonList(dto)); + HighRiskAssess mapped = new HighRiskAssess(); + mapped.setBenId(1L); + // No need to stub modelMapper.map(dto, HighRiskAssessDTO.class) as its result is not used + when(assessRepo.getByUserIdAndBenId(1L, 10)).thenReturn(null); + when(assessRepo.save(any(HighRiskAssess.class))).thenReturn(new HighRiskAssess()); + String result = highRiskService.saveAllAssessment(req); + assertTrue(result.contains("no of high risk assessment items saved: 1")); + } + + @Test + void saveAllAssessment_existingAssess_success() { + HighRiskAssessDTO dto = new HighRiskAssessDTO(); + dto.setBenId(2L); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(20); + req.setEntries(Collections.singletonList(dto)); + HighRiskAssess mapped = new HighRiskAssess(); + mapped.setBenId(2L); + HighRiskAssess existing = new HighRiskAssess(); + existing.setId(99L); + // No need to stub modelMapper.map(dto, HighRiskAssessDTO.class) as its result is not used + when(assessRepo.getByUserIdAndBenId(2L, 20)).thenReturn(existing); + when(assessRepo.save(any(HighRiskAssess.class))).thenReturn(existing); + String result = highRiskService.saveAllAssessment(req); + assertTrue(result.contains("no of high risk assessment items saved: 1")); + } + + @Test + void saveAllAssessment_exception() { + HighRiskAssessDTO dto = new HighRiskAssessDTO(); + dto.setBenId(3L); + UserDataDTO req = new UserDataDTO<>(); + req.setUserId(30); + req.setEntries(Collections.singletonList(dto)); + // No need to stub modelMapper.map(dto, HighRiskAssessDTO.class) as its result is not used + when(assessRepo.getByUserIdAndBenId(3L, 30)).thenThrow(new RuntimeException("fail")); + assertThrows(RuntimeException.class, () -> highRiskService.saveAllAssessment(req)); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/IncentiveServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/IncentiveServiceImplTest.java new file mode 100644 index 000000000..1038f1330 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/IncentiveServiceImplTest.java @@ -0,0 +1,128 @@ +package com.iemr.flw.service.impl; + +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.IncentiveActivity; +import com.iemr.flw.domain.iemr.IncentiveActivityRecord; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.*; +import com.iemr.flw.repo.iemr.IncentiveRecordRepo; +import com.iemr.flw.repo.iemr.IncentivesRepo; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; + +import java.sql.Timestamp; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class IncentiveServiceImplTest { + @Mock + private IncentivesRepo incentivesRepo; + @Mock + private IncentiveRecordRepo recordRepo; + @Mock + private ModelMapper modelMapper; + @InjectMocks + private IncentiveServiceImpl incentiveService; + + @Test + void saveIncentivesMaster_newActivity_success() { + IncentiveActivityDTO dto = new IncentiveActivityDTO(); + dto.setName("TestName"); + dto.setGroup("TestGroup"); + List dtos = Collections.singletonList(dto); + when(incentivesRepo.findIncentiveMasterByNameAndGroup("TestName", "TestGroup")).thenReturn(null); + doNothing().when(modelMapper).map(any(IncentiveActivityDTO.class), any(IncentiveActivity.class)); + when(incentivesRepo.save(any(IncentiveActivity.class))).thenReturn(new IncentiveActivity()); + String result = incentiveService.saveIncentivesMaster(dtos); + assertNotNull(result); + assertTrue(result.contains("saved master data for")); + } + + @Test + void saveIncentivesMaster_existingActivity_success() { + IncentiveActivityDTO dto = new IncentiveActivityDTO(); + dto.setName("TestName2"); + dto.setGroup("TestGroup2"); + List dtos = Collections.singletonList(dto); + IncentiveActivity existing = new IncentiveActivity(); + existing.setId(99L); + when(incentivesRepo.findIncentiveMasterByNameAndGroup("TestName2", "TestGroup2")).thenReturn(existing); + doNothing().when(modelMapper).map(any(IncentiveActivityDTO.class), any(IncentiveActivity.class)); + when(incentivesRepo.save(any(IncentiveActivity.class))).thenReturn(existing); + String result = incentiveService.saveIncentivesMaster(dtos); + assertNotNull(result); + assertTrue(result.contains("saved master data for")); + } + + @Test + void saveIncentivesMaster_exception() { + IncentiveActivityDTO dto = new IncentiveActivityDTO(); + dto.setName("TestName3"); + dto.setGroup("TestGroup3"); + List dtos = Collections.singletonList(dto); + when(incentivesRepo.findIncentiveMasterByNameAndGroup("TestName3", "TestGroup3")).thenThrow(new RuntimeException("fail")); + String result = incentiveService.saveIncentivesMaster(dtos); + assertNull(result); + } + + @Test + void getIncentiveMaster_success() { + IncentiveActivity activity = new IncentiveActivity(); + List activities = Arrays.asList(activity, activity); + when(incentivesRepo.findAll()).thenReturn(activities); + when(modelMapper.map(any(IncentiveActivity.class), eq(IncentiveActivityDTO.class))).thenReturn(new IncentiveActivityDTO()); + String json = incentiveService.getIncentiveMaster(new IncentiveRequestDTO()); + assertNotNull(json); + IncentiveActivityDTO[] arr = new Gson().fromJson(json, IncentiveActivityDTO[].class); + assertEquals(2, arr.length); + } + + @Test + void getIncentiveMaster_exception() { + when(incentivesRepo.findAll()).thenThrow(new RuntimeException("fail")); + String result = incentiveService.getIncentiveMaster(new IncentiveRequestDTO()); + assertNull(result); + } + + @Test + void getAllIncentivesByUserId_success() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + List entities = Arrays.asList(new IncentiveActivityRecord(), new IncentiveActivityRecord()); + when(recordRepo.findRecordsByAsha(ashaId, fromDate, toDate)).thenReturn(entities); + when(modelMapper.map(any(IncentiveActivityRecord.class), eq(IncentiveRecordDTO.class))).thenReturn(new IncentiveRecordDTO()); + String json = incentiveService.getAllIncentivesByUserId(req); + assertNotNull(json); + IncentiveRecordDTO[] arr = new Gson().fromJson(json, IncentiveRecordDTO[].class); + assertEquals(2, arr.length); + } + + @Test + void getAllIncentivesByUserId_emptyList() { + GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); + when(req.getFromDate()).thenReturn(fromDate); + when(req.getToDate()).thenReturn(toDate); + when(recordRepo.findRecordsByAsha(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); + String json = incentiveService.getAllIncentivesByUserId(req); + assertNotNull(json); + IncentiveRecordDTO[] arr = new Gson().fromJson(json, IncentiveRecordDTO[].class); + assertEquals(0, arr.length); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/InfantServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/InfantServiceImplTest.java index 04fac23e6..86772faf4 100644 --- a/src/test/java/com/iemr/flw/service/impl/InfantServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/InfantServiceImplTest.java @@ -4,72 +4,98 @@ import com.iemr.flw.domain.iemr.InfantRegister; import com.iemr.flw.dto.identity.GetBenRequestHandler; import com.iemr.flw.dto.iemr.InfantRegisterDTO; +import com.iemr.flw.repo.identity.BeneficiaryRepo; import com.iemr.flw.repo.iemr.InfantRegisterRepo; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +import org.modelmapper.ModelMapper; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +@ExtendWith(MockitoExtension.class) class InfantServiceImplTest { - @Mock private InfantRegisterRepo infantRegisterRepo; - + @Mock + private BeneficiaryRepo beneficiaryRepo; + @Mock + private ObjectMapper mapper; + @Mock + private ModelMapper modelMapper; @InjectMocks private InfantServiceImpl infantService; - private ObjectMapper mapper = new ObjectMapper(); + @Test + void registerInfant_newInfant_success() { + InfantRegisterDTO dto = new InfantRegisterDTO(); + dto.setBenId(1L); + dto.setBabyIndex(1); + List dtos = Collections.singletonList(dto); + when(infantRegisterRepo.findInfantRegisterByBenIdAndBabyIndexAndIsActive(1L, 1, true)).thenReturn(null); + doNothing().when(modelMapper).map(any(InfantRegisterDTO.class), any(InfantRegister.class)); + String result = infantService.registerInfant(dtos); + assertTrue(result.contains("no of infant register details saved: 1")); + } - @BeforeEach - void setUp() { - MockitoAnnotations.initMocks(this); + @Test + void registerInfant_existingInfant_success() { + InfantRegisterDTO dto = new InfantRegisterDTO(); + dto.setBenId(2L); + dto.setBabyIndex(2); + List dtos = Collections.singletonList(dto); + InfantRegister existing = new InfantRegister(); + existing.setId(99L); + when(infantRegisterRepo.findInfantRegisterByBenIdAndBabyIndexAndIsActive(2L, 2, true)).thenReturn(existing); + doNothing().when(modelMapper).map(any(InfantRegisterDTO.class), any(InfantRegister.class)); + + String result = infantService.registerInfant(dtos); + assertTrue(result.contains("no of infant register details saved: 1")); } -// @Test -// void testRegisterInfant_Success() { -// // Arrange -// List infantRegisterDTOs = new ArrayList<>(); -// infantRegisterDTOs.add(new InfantRegisterDTO()); -// -// // Act -// String result = infantService.registerInfant(infantRegisterDTOs); -// -// // Assert -// assertNotNull(result); -// assertEquals("saved successfully", result); -// -// verify(infantRegisterRepo, times(1)).save(anyList()); -// } -// -// @Test -// void testGetInfantDetails_Success() { -// // Arrange -// GetBenRequestHandler dto = new GetBenRequestHandler(); -// dto.setAshaId(123); -// dto.setFromDate(new Timestamp((new Date()).getTime())); -// dto.setToDate(new Timestamp((new Date()).getTime())); -// -// List infantRegisterList = new ArrayList<>(); -// infantRegisterList.add(new InfantRegister()); -// -// when(infantRegisterRepo.getInfantDetailsForUser(dto.getAshaId(), dto.getFromDate(), dto.getToDate())).thenReturn(infantRegisterList); -// -// // Act -// List result = infantService.getInfantDetails(dto); -// -// // Assert -// assertNotNull(result); -// assertEquals(infantRegisterList.size(), result.size()); -// -// verify(infantRegisterRepo, times(1)).getInfantDetailsForUser(dto.getAshaId(), dto.getFromDate(), dto.getToDate()); -// } + @Test + void registerInfant_exception() { + InfantRegisterDTO dto = new InfantRegisterDTO(); + dto.setBenId(3L); + dto.setBabyIndex(3); + List dtos = Collections.singletonList(dto); + when(infantRegisterRepo.findInfantRegisterByBenIdAndBabyIndexAndIsActive(3L, 3, true)).thenThrow(new RuntimeException("fail")); + String result = infantService.registerInfant(dtos); + assertNull(result); + } + + @Test + void getInfantDetails_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"); + List infantList = Arrays.asList(new InfantRegister(), new InfantRegister()); + when(infantRegisterRepo.getInfantDetailsForUser("user1", fromDate, toDate)).thenReturn(infantList); + when(mapper.convertValue(any(InfantRegister.class), eq(InfantRegisterDTO.class))).thenReturn(new InfantRegisterDTO()); + List result = infantService.getInfantDetails(dto); + assertNotNull(result); + assertEquals(2, result.size()); + } + + @Test + void getInfantDetails_exception() { + GetBenRequestHandler dto = mock(GetBenRequestHandler.class); + Integer ashaId = 123; + when(dto.getAshaId()).thenReturn(ashaId); + when(beneficiaryRepo.getUserName(ashaId)).thenThrow(new RuntimeException("fail")); + List result = infantService.getInfantDetails(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 fb37252c6..388893a2d 100644 --- a/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/MaternalHealthServiceImplTest.java @@ -1,106 +1,632 @@ -package com.iemr.flw.service.impl; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.iemr.flw.repo.iemr.ANCVisitRepo; -import com.iemr.flw.repo.iemr.PregnantWomanRegisterRepo; -import org.junit.jupiter.api.BeforeEach; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -class MaternalHealthServiceImplTest { - - @Mock - private PregnantWomanRegisterRepo pregnantWomanRegisterRepo; - - @Mock - private ANCVisitRepo ancVisitRepo; - - @InjectMocks - private MaternalHealthServiceImpl pregnantWomanService; - - private ObjectMapper mapper = new ObjectMapper(); - - @BeforeEach - void setUp() { - MockitoAnnotations.initMocks(this); - } - -// @Test -// void testRegisterPregnantWoman_Success() { -// // Arrange -// List pregnantWomanDTOs = new ArrayList<>(); -// pregnantWomanDTOs.add(new PregnantWomanDTO()); -// -// // Act -// String result = pregnantWomanService.registerPregnantWoman(pregnantWomanDTOs); +//package com.iemr.flw.service.impl; +// +//import com.iemr.flw.domain.iemr.*; +//import com.iemr.flw.dto.identity.GetBenRequestHandler; +//import com.iemr.flw.dto.iemr.*; +//import com.iemr.flw.repo.identity.BeneficiaryRepo; +//import com.iemr.flw.repo.iemr.*; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.extension.ExtendWith; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.junit.jupiter.MockitoExtension; +// +//import java.sql.Timestamp; +//import java.time.Instant; +//import java.util.*; +// +//import static org.junit.jupiter.api.Assertions.*; +//import static org.mockito.ArgumentMatchers.*; +//import static org.mockito.Mockito.*; +// +//@ExtendWith(MockitoExtension.class) +//class MaternalHealthServiceImplTest { +// +// @Mock +// private PregnantWomanRegisterRepo pregnantWomanRegisterRepo; +// @Mock +// private ANCVisitRepo ancVisitRepo; +// @Mock +// private AncCareRepo ancCareRepo; +// @Mock +// private PNCVisitRepo pncVisitRepo; +// @Mock +// private PNCCareRepo pncCareRepo; +// @Mock +// private BenVisitDetailsRepo benVisitDetailsRepo; +// @Mock +// private BeneficiaryRepo beneficiaryRepo; +// @Mock +// private PmsmaRepo pmsmaRepo; +// @Mock +// private IncentivesRepo incentivesRepo; +// @Mock +// private UserServiceRoleRepo userRepo; +// @Mock +// private IncentiveRecordRepo recordRepo; +// +// @InjectMocks +// private MaternalHealthServiceImpl service; +// +// private Timestamp currentTime; +// +// @BeforeEach +// void setUp() { +// currentTime = Timestamp.from(Instant.now()); +// } +// +// // registerPregnantWoman tests +// @Test +// void testRegisterPregnantWoman_newRecord_success() { +// // Arrange +// PregnantWomanDTO dto = createPregnantWomanDTO(); +// PregnantWomanRegister savedRegister = new PregnantWomanRegister(); +// savedRegister.setId(1L); +// +// when(pregnantWomanRegisterRepo.findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true)) +// .thenReturn(null); +// when(pregnantWomanRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedRegister)); +// +// // Act +// String result = service.registerPregnantWoman(Arrays.asList(dto)); // // // Assert -// assertNotNull(result); -// assertEquals("saved successfully", result); +// assertEquals("no of pwr details saved: 1", result); +// verify(pregnantWomanRegisterRepo).findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true); +// verify(pregnantWomanRegisterRepo).saveAll(anyList()); +// } +// +// @Test +// void testRegisterPregnantWoman_existingRecord_success() { +// // Arrange +// PregnantWomanDTO dto = createPregnantWomanDTO(); +// PregnantWomanRegister existingRegister = new PregnantWomanRegister(); +// existingRegister.setId(5L); +// +// when(pregnantWomanRegisterRepo.findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true)) +// .thenReturn(existingRegister); +// when(pregnantWomanRegisterRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingRegister)); +// +// // Act +// String result = service.registerPregnantWoman(Arrays.asList(dto)); // -// verify(pregnantWomanRegisterRepo, times(1)).save(anyList()); +// // Assert +// assertEquals("no of pwr details saved: 1", result); +// verify(pregnantWomanRegisterRepo).findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true); +// verify(pregnantWomanRegisterRepo).saveAll(anyList()); // } // // @Test -// void testGetPregnantWoman_Success() { +// void testRegisterPregnantWoman_exception() { // // Arrange -// GetBenRequestHandler dto = new GetBenRequestHandler(); -// dto.setAshaId(123); -// dto.setFromDate(new Timestamp((new Date()).getTime())); -// dto.setToDate(new Timestamp((new Date()).getTime())); +// PregnantWomanDTO dto = createPregnantWomanDTO(); +// when(pregnantWomanRegisterRepo.findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true)) +// .thenThrow(new RuntimeException("Database error")); // -// List pregnantWomanRegisterList = new ArrayList<>(); -// pregnantWomanRegisterList.add(new PregnantWomanRegister()); +// // Act +// String result = service.registerPregnantWoman(Arrays.asList(dto)); +// +// // Assert +// assertNull(result); +// } +// +// // getPregnantWoman tests +// @Test +// void testGetPregnantWoman_success() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// String userName = "testUser"; +// PregnantWomanRegister register = new PregnantWomanRegister(); +// register.setId(1L); +// register.setBenId(123L); // -// when(pregnantWomanRegisterRepo.getPWRWithBen(dto.getAshaId(), dto.getFromDate(), dto.getToDate())).thenReturn(pregnantWomanRegisterList); +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); +// when(pregnantWomanRegisterRepo.getPWRWithBen(userName, dto.getFromDate(), dto.getToDate())) +// .thenReturn(Arrays.asList(register)); // // // Act -// List result = pregnantWomanService.getPregnantWoman(dto); +// List result = service.getPregnantWoman(dto); // // // Assert // assertNotNull(result); -// assertEquals(pregnantWomanRegisterList.size(), result.size()); +// assertEquals(1, result.size()); +// verify(beneficiaryRepo).getUserName(dto.getAshaId()); +// verify(pregnantWomanRegisterRepo).getPWRWithBen(userName, dto.getFromDate(), dto.getToDate()); +// } +// +// @Test +// void testGetPregnantWoman_exception() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenThrow(new RuntimeException("Database error")); +// +// // Act +// List result = service.getPregnantWoman(dto); // -// verify(pregnantWomanRegisterRepo, times(1)).getPWRWithBen(dto.getAshaId(), dto.getFromDate(), dto.getToDate()); +// // Assert +// assertNull(result); // } // +// // getANCVisits tests // @Test -// void testGetANCVisits_Success() { +// void testGetANCVisits_success() { // // Arrange -// GetBenRequestHandler dto = new GetBenRequestHandler(); -// dto.setAshaId(123); -// dto.setFromDate(new Timestamp((new Date()).getTime())); -// dto.setToDate(new Timestamp((new Date()).getTime())); +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// String userName = "testUser"; +// ANCVisit ancVisit = new ANCVisit(); +// ancVisit.setId(1L); +// ancVisit.setBenId(123L); +// +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); +// when(ancVisitRepo.getANCForPW(userName, dto.getFromDate(), dto.getToDate())) +// .thenReturn(Arrays.asList(ancVisit)); +// +// // Act +// List result = service.getANCVisits(dto); // -// List ancVisits = new ArrayList<>(); -// ancVisits.add(new ANCVisit()); +// // Assert +// assertNotNull(result); +// assertEquals(1, result.size()); +// verify(beneficiaryRepo).getUserName(dto.getAshaId()); +// verify(ancVisitRepo).getANCForPW(userName, dto.getFromDate(), dto.getToDate()); +// } // -// when(ancVisitRepo.getANCForPW(dto.getAshaId(), dto.getFromDate(), dto.getToDate())).thenReturn(ancVisits); +// @Test +// void testGetANCVisits_exception() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenThrow(new RuntimeException("Database error")); // // // Act -// List result = pregnantWomanService.getANCVisits(dto); +// List result = service.getANCVisits(dto); +// +// // Assert +// assertNull(result); +// } +// +// // saveANCVisit tests +// @Test +// void testSaveANCVisit_newRecord_success() { +// // Arrange +// ANCVisitDTO dto = createANCVisitDTO(); +// BenVisitDetail savedVisitDetail = new BenVisitDetail(); +// savedVisitDetail.setBenVisitId(1L); +// +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) +// .thenReturn(null); +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBenId())).thenReturn(100L); +// when(pregnantWomanRegisterRepo.findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true)) +// .thenReturn(null); +// when(benVisitDetailsRepo.save(any(BenVisitDetail.class))).thenReturn(savedVisitDetail); +// when(ancVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(new ANCVisit())); +// when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList(new AncCare())); +// +// // Act +// String result = service.saveANCVisit(Arrays.asList(dto)); +// +// // Assert +// assertEquals("no of anc details saved: 1", result); +// verify(ancVisitRepo).findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true); +// verify(beneficiaryRepo).getRegIDFromBenId(dto.getBenId()); +// verify(benVisitDetailsRepo).save(any(BenVisitDetail.class)); +// verify(ancVisitRepo).saveAll(anyList()); +// verify(ancCareRepo).saveAll(anyList()); +// } +// +// @Test +// void testSaveANCVisit_newRecordWithPregnantWomanRegister_success() { +// // Arrange +// ANCVisitDTO dto = createANCVisitDTO(); +// PregnantWomanRegister pwr = new PregnantWomanRegister(); +// pwr.setLmpDate(currentTime); +// BenVisitDetail savedVisitDetail = new BenVisitDetail(); +// savedVisitDetail.setBenVisitId(1L); +// +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) +// .thenReturn(null); +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBenId())).thenReturn(100L); +// when(pregnantWomanRegisterRepo.findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true)) +// .thenReturn(pwr); +// when(benVisitDetailsRepo.save(any(BenVisitDetail.class))).thenReturn(savedVisitDetail); +// when(ancVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(new ANCVisit())); +// when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList(new AncCare())); +// +// // Act +// String result = service.saveANCVisit(Arrays.asList(dto)); +// +// // Assert +// assertEquals("no of anc details saved: 1", result); +// } +// +// @Test +// void testSaveANCVisit_existingRecord_success() { +// // Arrange +// ANCVisitDTO dto = createANCVisitDTO(); +// ANCVisit existingVisit = new ANCVisit(); +// existingVisit.setId(5L); +// +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) +// .thenReturn(existingVisit); +// when(ancVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingVisit)); +// when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList()); +// +// // Act +// String result = service.saveANCVisit(Arrays.asList(dto)); +// +// // Assert +// assertEquals("no of anc details saved: 1", result); +// verify(ancVisitRepo).findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true); +// verify(ancVisitRepo).saveAll(anyList()); +// } +// +// @Test +// void testSaveANCVisit_withIncentives_anc1() { +// // Arrange +// ANCVisitDTO dto = createANCVisitDTO(); +// dto.setAncVisit(1); // First ANC visit +// BenVisitDetail savedVisitDetail = new BenVisitDetail(); +// savedVisitDetail.setBenVisitId(1L); +// +// ANCVisit savedAncVisit = new ANCVisit(); +// savedAncVisit.setAncVisit(1); +// savedAncVisit.setBenId(dto.getBenId()); +// savedAncVisit.setCreatedDate(currentTime); +// savedAncVisit.setCreatedBy("testUser"); +// +// IncentiveActivity anc1Activity = new IncentiveActivity(); +// anc1Activity.setId(1L); +// anc1Activity.setRate(100); +// +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) +// .thenReturn(null); +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBenId())).thenReturn(100L); +// when(pregnantWomanRegisterRepo.findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true)) +// .thenReturn(null); +// when(benVisitDetailsRepo.save(any(BenVisitDetail.class))).thenReturn(savedVisitDetail); +// when(ancVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedAncVisit)); +// when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList(new AncCare())); +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANC-1", "MATERNAL HEALTH")) +// .thenReturn(anc1Activity); +// when(userRepo.getUserIdByName("testUser")).thenReturn(1); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(1L, currentTime, dto.getBenId())) +// .thenReturn(null); +// +// // Act +// String result = service.saveANCVisit(Arrays.asList(dto)); +// +// // Assert +// assertEquals("no of anc details saved: 1", result); +// verify(recordRepo).save(any(IncentiveActivityRecord.class)); +// } +// +// @Test +// void testSaveANCVisit_withIncentives_anc4() { +// // Arrange +// ANCVisitDTO dto = createANCVisitDTO(); +// dto.setAncVisit(4); // Fourth ANC visit +// +// ANCVisit existingAncVisit = new ANCVisit(); +// existingAncVisit.setId(1L); +// existingAncVisit.setAncVisit(4); +// existingAncVisit.setBenId(dto.getBenId()); +// existingAncVisit.setCreatedDate(currentTime); +// existingAncVisit.setCreatedBy("testUser"); +// +// IncentiveActivity ancFullActivity = new IncentiveActivity(); +// ancFullActivity.setId(2L); +// ancFullActivity.setRate(200); +// +// // Create mock ANC visits for all 4 visits +// ANCVisit visit1 = new ANCVisit(); +// visit1.setCreatedDate(currentTime); +// ANCVisit visit2 = new ANCVisit(); +// ANCVisit visit3 = new ANCVisit(); +// ANCVisit visit4 = new ANCVisit(); +// visit4.setCreatedDate(currentTime); +// +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) +// .thenReturn(existingAncVisit); +// when(ancVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingAncVisit)); +// when(ancCareRepo.saveAll(anyList())).thenReturn(Arrays.asList()); +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANC-1", "MATERNAL HEALTH")) +// .thenReturn(new IncentiveActivity()); // Need to return a non-null activity for processing to continue +// when(incentivesRepo.findIncentiveMasterByNameAndGroup("ANC-FULL", "MATERNAL HEALTH")) +// .thenReturn(ancFullActivity); +// when(userRepo.getUserIdByName("testUser")).thenReturn(1); +// when(recordRepo.findRecordByActivityIdCreatedDateBenId(2L, currentTime, dto.getBenId())) +// .thenReturn(null); +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 1, true)) +// .thenReturn(visit1); +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 2, true)) +// .thenReturn(visit2); +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 3, true)) +// .thenReturn(visit3); +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), 4, true)) +// .thenReturn(visit4); +// +// // Act +// String result = service.saveANCVisit(Arrays.asList(dto)); +// +// // Assert +// assertEquals("no of anc details saved: 1", result); +// verify(recordRepo).save(any(IncentiveActivityRecord.class)); +// } +// +// @Test +// void testSaveANCVisit_exception() { +// // Arrange +// ANCVisitDTO dto = createANCVisitDTO(); +// when(ancVisitRepo.findANCVisitByBenIdAndAncVisitAndIsActive(dto.getBenId(), dto.getAncVisit(), true)) +// .thenThrow(new RuntimeException("Database error")); +// +// // Act +// String result = service.saveANCVisit(Arrays.asList(dto)); +// +// // Assert +// assertNull(result); +// } +// +// // getPmsmaRecords tests +// @Test +// void testGetPmsmaRecords_success() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// String userName = "testUser"; +// PMSMA pmsma = new PMSMA(); +// pmsma.setId(1L); +// pmsma.setBenId(123L); +// +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); +// when(pmsmaRepo.getAllPmsmaByAshaId(userName, dto.getFromDate(), dto.getToDate())) +// .thenReturn(Arrays.asList(pmsma)); +// +// // Act +// List result = service.getPmsmaRecords(dto); // // // Assert // assertNotNull(result); -// assertEquals(ancVisits.size(), result.size()); +// assertEquals(1, result.size()); +// verify(beneficiaryRepo).getUserName(dto.getAshaId()); +// verify(pmsmaRepo).getAllPmsmaByAshaId(userName, dto.getFromDate(), dto.getToDate()); +// } +// +// @Test +// void testGetPmsmaRecords_exception() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenThrow(new RuntimeException("Database error")); +// +// // Act +// List result = service.getPmsmaRecords(dto); +// +// // Assert +// assertNull(result); +// } +// +// // savePmsmaRecords tests +// @Test +// void testSavePmsmaRecords_newRecord_success() { +// // Arrange +// PmsmaDTO dto = createPmsmaDTO(); +// PMSMA savedPmsma = new PMSMA(); +// savedPmsma.setId(1L); +// +// when(pmsmaRepo.findPMSMAByBenIdAndIsActive(dto.getBenId(), true)).thenReturn(null); +// when(pmsmaRepo.saveAll(anyList())).thenReturn(Arrays.asList(savedPmsma)); +// +// // Act +// String result = service.savePmsmaRecords(Arrays.asList(dto)); +// +// // Assert +// assertEquals("No. of PMSMA records saved: 1", result); +// verify(pmsmaRepo).findPMSMAByBenIdAndIsActive(dto.getBenId(), true); +// verify(pmsmaRepo).saveAll(anyList()); +// } +// +// @Test +// void testSavePmsmaRecords_existingRecord_success() { +// // Arrange +// PmsmaDTO dto = createPmsmaDTO(); +// PMSMA existingPmsma = new PMSMA(); +// existingPmsma.setId(5L); +// +// when(pmsmaRepo.findPMSMAByBenIdAndIsActive(dto.getBenId(), true)).thenReturn(existingPmsma); +// when(pmsmaRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingPmsma)); +// +// // Act +// String result = service.savePmsmaRecords(Arrays.asList(dto)); +// +// // Assert +// assertEquals("No. of PMSMA records saved: 1", result); +// verify(pmsmaRepo).findPMSMAByBenIdAndIsActive(dto.getBenId(), true); +// verify(pmsmaRepo).saveAll(anyList()); +// } +// +// @Test +// void testSavePmsmaRecords_exception() { +// // Arrange +// PmsmaDTO dto = createPmsmaDTO(); +// when(pmsmaRepo.findPMSMAByBenIdAndIsActive(dto.getBenId(), true)) +// .thenThrow(new RuntimeException("Database error")); // -// verify(ancVisitRepo, times(1)).getANCForPW(dto.getAshaId(), dto.getFromDate(), dto.getToDate()); +// // Act +// String result = service.savePmsmaRecords(Arrays.asList(dto)); +// +// // Assert +// assertNull(result); // } // +// // getPNCVisits tests // @Test -// void testSaveANCVisit_Success() { +// void testGetPNCVisits_success() { // // Arrange -// List ancVisitDTOs = new ArrayList<>(); -// ancVisitDTOs.add(new ANCVisitDTO()); +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// String userName = "testUser"; +// PNCVisit pncVisit = new PNCVisit(); +// pncVisit.setId(1L); +// pncVisit.setBenId(123L); +// +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenReturn(userName); +// when(pncVisitRepo.getPNCForPW(userName, dto.getFromDate(), dto.getToDate())) +// .thenReturn(Arrays.asList(pncVisit)); // // // Act -// String result = pregnantWomanService.saveANCVisit(ancVisitDTOs); +// List result = service.getPNCVisits(dto); // // // Assert // assertNotNull(result); -// assertEquals("saved successfully", result); +// assertEquals(1, result.size()); +// verify(beneficiaryRepo).getUserName(dto.getAshaId()); +// verify(pncVisitRepo).getPNCForPW(userName, dto.getFromDate(), dto.getToDate()); +// } +// +// @Test +// void testGetPNCVisits_exception() { +// // Arrange +// GetBenRequestHandler dto = createGetBenRequestHandler(); +// when(beneficiaryRepo.getUserName(dto.getAshaId())).thenThrow(new RuntimeException("Database error")); +// +// // Act +// List result = service.getPNCVisits(dto); +// +// // Assert +// assertNull(result); +// } +// +// // savePNCVisit tests +// @Test +// void testSavePNCVisit_newRecord_success() { +// // Arrange +// PNCVisitDTO dto = createPNCVisitDTO(); +// BenVisitDetail savedVisitDetail = new BenVisitDetail(); +// savedVisitDetail.setBenVisitId(1L); +// +// when(pncVisitRepo.findPNCVisitByBenIdAndPncPeriodAndIsActive(dto.getBenId(), dto.getPncPeriod(), true)) +// .thenReturn(null); +// when(beneficiaryRepo.getRegIDFromBenId(dto.getBenId())).thenReturn(100L); +// when(pregnantWomanRegisterRepo.findPregnantWomanRegisterByBenIdAndIsActive(dto.getBenId(), true)) +// .thenReturn(null); +// when(benVisitDetailsRepo.save(any(BenVisitDetail.class))).thenReturn(savedVisitDetail); +// when(pncVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(new PNCVisit())); +// when(pncCareRepo.saveAll(anyList())).thenReturn(Arrays.asList(new PNCCare())); +// +// // Act +// String result = service.savePNCVisit(Arrays.asList(dto)); +// +// // Assert +// assertEquals("no of pnc details saved: 1", result); +// verify(pncVisitRepo).findPNCVisitByBenIdAndPncPeriodAndIsActive(dto.getBenId(), dto.getPncPeriod(), true); +// verify(beneficiaryRepo).getRegIDFromBenId(dto.getBenId()); +// verify(benVisitDetailsRepo).save(any(BenVisitDetail.class)); +// verify(pncVisitRepo).saveAll(anyList()); +// verify(pncCareRepo).saveAll(anyList()); +// } +// +// @Test +// void testSavePNCVisit_existingRecord_success() { +// // Arrange +// PNCVisitDTO dto = createPNCVisitDTO(); +// PNCVisit existingVisit = new PNCVisit(); +// existingVisit.setId(5L); +// +// when(pncVisitRepo.findPNCVisitByBenIdAndPncPeriodAndIsActive(dto.getBenId(), dto.getPncPeriod(), true)) +// .thenReturn(existingVisit); +// when(pncVisitRepo.saveAll(anyList())).thenReturn(Arrays.asList(existingVisit)); +// when(pncCareRepo.saveAll(anyList())).thenReturn(Arrays.asList()); +// +// // Act +// String result = service.savePNCVisit(Arrays.asList(dto)); +// +// // Assert +// assertEquals("no of pnc details saved: 1", result); +// verify(pncVisitRepo).findPNCVisitByBenIdAndPncPeriodAndIsActive(dto.getBenId(), dto.getPncPeriod(), true); +// verify(pncVisitRepo).saveAll(anyList()); +// } +// +// @Test +// void testSavePNCVisit_exception() { +// // Arrange +// PNCVisitDTO dto = createPNCVisitDTO(); +// when(pncVisitRepo.findPNCVisitByBenIdAndPncPeriodAndIsActive(dto.getBenId(), dto.getPncPeriod(), true)) +// .thenThrow(new RuntimeException("Database error")); +// +// // Act +// String result = service.savePNCVisit(Arrays.asList(dto)); +// +// // Assert +// assertNull(result); +// } +// +// // Helper methods to create test data +// private PregnantWomanDTO createPregnantWomanDTO() { +// PregnantWomanDTO dto = new PregnantWomanDTO(); +// dto.setBenId(123L); +// dto.setLmpDate(currentTime); +// dto.setBloodGroup("A+"); +// dto.setWeight(60); +// dto.setHeight(160); +// dto.setIsActive(true); +// dto.setCreatedBy("testUser"); +// dto.setCreatedDate(currentTime); +// dto.setUpdatedBy("testUser"); +// dto.setUpdatedDate(currentTime); +// return dto; +// } +// +// private GetBenRequestHandler createGetBenRequestHandler() { +// GetBenRequestHandler dto = new GetBenRequestHandler(); +// dto.setAshaId(1); +// dto.setFromDate(currentTime); +// dto.setToDate(currentTime); +// return dto; +// } +// +// private ANCVisitDTO createANCVisitDTO() { +// ANCVisitDTO dto = new ANCVisitDTO(); +// dto.setBenId(123L); +// dto.setAncDate(currentTime); +// dto.setAncVisit(1); +// dto.setWeightOfPW(65); +// dto.setBpSystolic(120); +// dto.setBpDiastolic(80); +// dto.setIsActive(true); +// dto.setCreatedDate(currentTime); +// dto.setCreatedBy("testUser"); +// dto.setUpdatedDate(currentTime); +// dto.setUpdatedBy("testUser"); +// dto.setProviderServiceMapID(1); +// return dto; +// } +// +// private PmsmaDTO createPmsmaDTO() { +// PmsmaDTO dto = new PmsmaDTO(); +// dto.setBenId(123L); +// dto.setRchNumber("RCH123"); +// dto.setHusbandName("Test Husband"); +// dto.setAddress("Test Address"); +// dto.setWeight(65); +// dto.setIsActive(true); +// dto.setCreatedBy("testUser"); +// dto.setCreatedDate(currentTime); +// dto.setUpdatedDate(currentTime); +// dto.setUpdatedBy("testUser"); +// return dto; +// } // -// verify(ancVisitRepo, times(1)).save(anyList()); +// private PNCVisitDTO createPNCVisitDTO() { +// PNCVisitDTO dto = new PNCVisitDTO(); +// dto.setBenId(123L); +// dto.setPncPeriod(0); // First day +// dto.setPncDate(currentTime); +// dto.setIfaTabsGiven(10); +// dto.setAnyContraceptionMethod(false); +// dto.setMotherDeath(false); +// dto.setIsActive(true); +// dto.setCreatedDate(currentTime); +// dto.setCreatedBy("testUser"); +// dto.setUpdatedDate(currentTime); +// dto.setUpdatedBy("testUser"); +// return dto; // } -} +//} diff --git a/src/test/java/com/iemr/flw/service/impl/TBScreeningServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/TBScreeningServiceImplTest.java new file mode 100644 index 000000000..3ed2bb874 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/TBScreeningServiceImplTest.java @@ -0,0 +1,118 @@ +//package com.iemr.flw.service.impl; +// +//import com.google.gson.Gson; +//import com.iemr.flw.domain.iemr.TBScreening; +//import com.iemr.flw.dto.identity.GetBenRequestHandler; +//import com.iemr.flw.dto.iemr.TBScreeningDTO; +//import com.iemr.flw.dto.iemr.TBScreeningRequestDTO; +//import com.iemr.flw.repo.iemr.TBScreeningRepo; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.extension.ExtendWith; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.junit.jupiter.MockitoExtension; +//import org.modelmapper.ModelMapper; +// +//import java.sql.Timestamp; +//import java.util.*; +// +//import static org.junit.jupiter.api.Assertions.*; +//import static org.mockito.ArgumentMatchers.*; +//import static org.mockito.Mockito.*; +// +//@ExtendWith(MockitoExtension.class) +//class TBScreeningServiceImplTest { +// @Mock +// private TBScreeningRepo tbScreeningRepo; +// @Mock +// private ModelMapper modelMapper; +// @InjectMocks +// private TBScreeningServiceImpl tbScreeningService; +// +// @BeforeEach +// void setUp() { +// // inject mock modelMapper +// // Reflection is not needed as modelMapper is final, but we can set via constructor if needed +// } +// +// @Test +// void getByBenId_returnsNull() throws Exception { +// String result = tbScreeningService.getByBenId(1L, "auth"); +// assertNull(result); +// } +// +// @Test +// void save_newTBScreening_success() throws Exception { +// TBScreeningDTO dto = new TBScreeningDTO(); +// dto.setBenId(1L); +// TBScreeningRequestDTO req = new TBScreeningRequestDTO(); +// req.setUserId(10); +// req.setTbScreeningList(Collections.singletonList(dto)); +// when(tbScreeningRepo.getByUserIdAndBenId(1L, 10)).thenReturn(null); +// when(tbScreeningRepo.save(any(TBScreening.class))).thenReturn(new TBScreening()); +// String result = tbScreeningService.save(req); +// assertTrue(result.contains("no of tb screening items saved: 1")); +// } +// +// @Test +// void save_existingTBScreening_success() throws Exception { +// TBScreeningDTO dto = new TBScreeningDTO(); +// dto.setBenId(2L); +// TBScreeningRequestDTO req = new TBScreeningRequestDTO(); +// req.setUserId(20); +// req.setTbScreeningList(Collections.singletonList(dto)); +// TBScreening existing = new TBScreening(); +// existing.setId(99L); +// when(tbScreeningRepo.getByUserIdAndBenId(2L, 20)).thenReturn(existing); +// when(tbScreeningRepo.save(any(TBScreening.class))).thenReturn(existing); +// String result = tbScreeningService.save(req); +// assertTrue(result.contains("no of tb screening items saved: 1")); +// } +// +// @Test +// void save_exception() throws Exception { +// TBScreeningDTO dto = new TBScreeningDTO(); +// dto.setBenId(3L); +// TBScreeningRequestDTO req = new TBScreeningRequestDTO(); +// req.setUserId(30); +// req.setTbScreeningList(Collections.singletonList(dto)); +// when(tbScreeningRepo.getByUserIdAndBenId(3L, 30)).thenThrow(new RuntimeException("fail")); +// assertThrows(Exception.class, () -> tbScreeningService.save(req)); +// } +// +// @Test +// void getByUserId_success() { +// GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); +// 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); +// String json = tbScreeningService.getByUserId(req); +// assertNotNull(json); +// TBScreeningRequestDTO result = new Gson().fromJson(json, TBScreeningRequestDTO.class); +// assertEquals(2, result.getTbScreeningList().size()); +// assertEquals(ashaId, result.getUserId()); +// } +// +// @Test +// void getByUserId_emptyList() { +// GetBenRequestHandler req = 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(req.getAshaId()).thenReturn(ashaId); +// when(req.getFromDate()).thenReturn(fromDate); +// when(req.getToDate()).thenReturn(toDate); +// when(tbScreeningRepo.getByUserId(ashaId, fromDate, toDate)).thenReturn(Collections.emptyList()); +// String json = tbScreeningService.getByUserId(req); +// assertNotNull(json); +// TBScreeningRequestDTO result = new Gson().fromJson(json, TBScreeningRequestDTO.class); +// assertEquals(0, result.getTbScreeningList().size()); +// assertEquals(ashaId, result.getUserId()); +// } +//} diff --git a/src/test/java/com/iemr/flw/service/impl/TBSuspectedServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/TBSuspectedServiceImplTest.java new file mode 100644 index 000000000..b6259516b --- /dev/null +++ b/src/test/java/com/iemr/flw/service/impl/TBSuspectedServiceImplTest.java @@ -0,0 +1,91 @@ +package com.iemr.flw.service.impl; + +import com.google.gson.Gson; +import com.iemr.flw.domain.iemr.TBSuspected; +import com.iemr.flw.dto.identity.GetBenRequestHandler; +import com.iemr.flw.dto.iemr.TBSuspectedDTO; +import com.iemr.flw.dto.iemr.TBSuspectedRequestDTO; +import com.iemr.flw.repo.iemr.TBSuspectedRepo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +class TBSuspectedServiceImplTest { + @Mock + private TBSuspectedRepo tbSuspectedRepo; + + @InjectMocks + private TBSuspectedServiceImpl service; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void getByBenId_alwaysReturnsNull() throws Exception { + assertNull(service.getByBenId(1L, "auth")); + } + + @Test + void save_newAndExistingSuspected() throws Exception { + TBSuspectedDTO dto1 = new TBSuspectedDTO(); + dto1.setBenId(1L); + TBSuspectedDTO dto2 = new TBSuspectedDTO(); + dto2.setBenId(2L); + TBSuspectedRequestDTO req = new TBSuspectedRequestDTO(); + req.setUserId(100); // Integer + req.setTbSuspectedList(Arrays.asList(dto1, dto2)); + + // First is new, second is existing + when(tbSuspectedRepo.getByUserIdAndBenId(1L, 100)).thenReturn(null); + TBSuspected existing = new TBSuspected(); + existing.setId(99L); + when(tbSuspectedRepo.getByUserIdAndBenId(2L, 100)).thenReturn(existing); + + String result = service.save(req); + assertTrue(result.contains("no of tb suspected items saved:2")); + verify(tbSuspectedRepo, times(2)).save(any(TBSuspected.class)); + } + + @Test + void getByUserId_returnsJsonWithCorrectUserIdAndList() { + GetBenRequestHandler req = new GetBenRequestHandler(); + req.setAshaId(123); // Integer + Timestamp from = Timestamp.valueOf("2023-01-01 00:00:00"); + Timestamp to = Timestamp.valueOf("2023-12-31 00:00:00"); + req.setFromDate(from); + req.setToDate(to); + TBSuspected suspected = new TBSuspected(); + suspected.setId(1L); + when(tbSuspectedRepo.getByUserId(123, from, to)).thenReturn(Collections.singletonList(suspected)); + String json = service.getByUserId(req); + assertTrue(json.contains("\"userId\":123")); + assertTrue(json.contains("tbSuspectedList")); + } + + @Test + void getByUserId_emptyList() { + GetBenRequestHandler req = new GetBenRequestHandler(); + req.setAshaId(123); // Integer + Timestamp from = Timestamp.valueOf("2023-01-01 00:00:00"); + Timestamp to = Timestamp.valueOf("2023-12-31 00:00:00"); + req.setFromDate(from); + req.setToDate(to); + when(tbSuspectedRepo.getByUserId(anyInt(), any(Timestamp.class), any(Timestamp.class))).thenReturn(Collections.emptyList()); + String json = service.getByUserId(req); + assertTrue(json.contains("tbSuspectedList")); + } +} diff --git a/src/test/java/com/iemr/flw/service/impl/UserServiceImplTest.java b/src/test/java/com/iemr/flw/service/impl/UserServiceImplTest.java index 8d204e40c..3bf8772d7 100644 --- a/src/test/java/com/iemr/flw/service/impl/UserServiceImplTest.java +++ b/src/test/java/com/iemr/flw/service/impl/UserServiceImplTest.java @@ -7,16 +7,12 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; +import java.util.Collections; import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; class UserServiceImplTest { - @Mock private UserServiceRoleRepo userServiceRoleRepo; @@ -25,27 +21,21 @@ class UserServiceImplTest { @BeforeEach void setUp() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test - void testGetUserDetail_Success() { - // Arrange - int userId = 123; - UserServiceRoleDTO expectedUserRoleDTO = new UserServiceRoleDTO(); - expectedUserRoleDTO.setUserId(userId); - expectedUserRoleDTO.setRoleName("ROLE_USER"); - - List userRoleList = new ArrayList<>(); - userRoleList.add(expectedUserRoleDTO); - - when(userServiceRoleRepo.getUserRole(anyInt())).thenReturn(userRoleList); - - // Act - UserServiceRoleDTO result = userService.getUserDetail(userId); + void getUserDetail_returnsUserRole() { + UserServiceRoleDTO dto = new UserServiceRoleDTO(); + when(userServiceRoleRepo.getUserRole(123)).thenReturn(Collections.singletonList(dto)); + UserServiceRoleDTO result = userService.getUserDetail(123); + assertSame(dto, result); + verify(userServiceRoleRepo).getUserRole(123); + } - // Assert - assertEquals(expectedUserRoleDTO.getUserId(), result.getUserId()); - assertEquals(expectedUserRoleDTO.getRoleName(), result.getRoleName()); + @Test + void getUserDetail_emptyList_throwsException() { + when(userServiceRoleRepo.getUserRole(456)).thenReturn(Collections.emptyList()); + assertThrows(IndexOutOfBoundsException.class, () -> userService.getUserDetail(456)); } } diff --git a/src/test/java/com/iemr/flw/service/sync/StopTBSyncCoverageTest.java b/src/test/java/com/iemr/flw/service/sync/StopTBSyncCoverageTest.java new file mode 100644 index 000000000..dea113d81 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/sync/StopTBSyncCoverageTest.java @@ -0,0 +1,106 @@ +package com.iemr.flw.service.sync; + +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Every table FLW-API maps must be either synced to STOP-TB tablets through the change log + * (listed in a {@link StopTBSyncType}'s sourceTables, which the hub checks for triggers) or listed + * below as deliberately not synced. A new entity fails this test until someone decides which, so a + * STOP-TB table can't be added without its change-log triggers. + */ +class StopTBSyncCoverageTest { + + /** STOP-TB-adjacent tables that the app does not pull, with the reason. */ + static final Map NOT_SYNCED_STOPTB = Map.ofEntries( + Map.entry("db_iemr.t_phy_generalexam", "copy of the general examination for the standard tables; the app pulls tb_stoptb_general_examination"), + Map.entry("db_iemr.t_benreferdetails", "copy written on save for the standard tables; not read by any STOP-TB pull"), + Map.entry("db_iemr.tb_stoptb_registration", "not read by any service"), + Map.entry("db_iemr.tb_diagnostic_document", "the app does not pull documents"), + Map.entry("db_iemr.tb_diagnostic_provider_token", "vendor credentials, server only"), + Map.entry("db_identity.m_beneficiaryregidmapping", "BenRegId/BeneficiaryID pool, fixed once assigned"), + Map.entry("db_iemr.t_dynamic_form", "form definitions: master data, pulled separately"), + Map.entry("db_iemr.t_form_version", "form definitions: master data, pulled separately"), + Map.entry("db_iemr.t_form_section", "form definitions: master data, pulled separately"), + Map.entry("db_iemr.t_section_question", "form definitions: master data, pulled separately"), + Map.entry("db_iemr.t_question_option", "form definitions: master data, pulled separately"), + Map.entry("db_iemr.t_question_validation", "form definitions: master data, pulled separately"), + Map.entry("db_iemr.t_option_condition", "form definitions: master data, pulled separately"), + Map.entry("db_iemr.t_dynamic_form_migration_log", "server-side bookkeeping"), + Map.entry("db_iemr.m_user", "user master, read at login"), + Map.entry("db_iemr.v_userservicerolemapping", "view of user roles, read at login"), + Map.entry("db_iemr.general_opd_entry", "not part of the STOP-TB pull")); + + /** Tables of other FLW programmes (ASHA app), not used by STOP-TB. */ + static final Set OTHER_PROGRAMMES = Set.of( + "db_iemr.adolescent_health", "db_iemr.ahd_form", "db_iemr.anc_counselling_care", "db_iemr.asha_profile", + "db_iemr.asha_supervisor_mapping", "db_iemr.campaign_filariasis_mda", "db_iemr.campaign_ors", + "db_iemr.campaign_pulse_polio", "db_iemr.cdtf_visit_details", "db_iemr.deworming_form", + "db_iemr.eligible_couple_tracking", "db_iemr.high_risk_assess", "db_iemr.i_mobilization_mosquito_net", + "db_iemr.incentive_activity_record", "db_iemr.incentive_pending_activity", "db_iemr.irs_round", + "db_iemr.leprosy_follow_up", "db_iemr.m_immunizationservicevaccination", "db_iemr.m_incentive_activity", + "db_iemr.m_incentive_activity_lang_mapping", "db_iemr.m_otp_beneficiary", "db_iemr.m_smstemplate", + "db_iemr.maa_meeting", "db_iemr.malaria_follow_up", "db_iemr.non_pregnant_high_risk_assess", + "db_iemr.non_pregnant_high_risk_track", "db_iemr.phc_review_meeting", "db_iemr.pregnant_high_risk_assess", + "db_iemr.pregnant_high_risk_track", "db_iemr.sammelan_attachment", "db_iemr.sammelan_record", "db_iemr.screening_aesje", + "db_iemr.screening_filaria", "db_iemr.screening_kala_azar", "db_iemr.screening_leprosy", + "db_iemr.screening_malaria", "db_iemr.t_anc_visit", "db_iemr.t_anccare", "db_iemr.t_cbacdetails", + "db_iemr.t_cdr", "db_iemr.t_child_register", "db_iemr.t_childvaccinedetail1", "db_iemr.t_delivery_outcome", + "db_iemr.t_eligible_couple_register", "db_iemr.t_eye_checkup", "db_iemr.t_hbnc_part1", "db_iemr.t_hbnc_part2", + "db_iemr.t_hbnc_visit", "db_iemr.t_hbnc_visit_card", "db_iemr.t_hbyc", "db_iemr.t_hbyc_child_visits", + "db_iemr.t_ifa_distribution", "db_iemr.t_ifa_distribution_data", "db_iemr.t_infant_register", + "db_iemr.t_mda_distribution_data", "db_iemr.t_mdsr", "db_iemr.t_micro_birth_plan", "db_iemr.t_ors_distribution", + "db_iemr.t_pmsma", "db_iemr.t_pnc_visit", "db_iemr.t_pnccare", "db_iemr.t_pregnant_woman_register", + "db_iemr.t_sam_visit", "db_iemr.uwin_session_record", "db_iemr.vhnc_form", "db_iemr.vhnd_form", + "db_identity.i_cbac_additional_details", "db_identity.i_cbacdetails"); + + @Test + void everyMappedTableIsSyncedOrDeliberatelyNot() throws Exception { + Set synced = new HashSet<>(); + for (StopTBSyncType t : StopTBSyncType.values()) synced.addAll(t.sourceTables()); + + List unclassified = new ArrayList<>(); + for (String table : mappedTables()) { + if (!synced.contains(table) && !NOT_SYNCED_STOPTB.containsKey(table) && !OTHER_PROGRAMMES.contains(table)) { + unclassified.add(table); + } + } + Collections.sort(unclassified); + assertTrue(unclassified.isEmpty(), "Tables neither synced to STOP-TB tablets nor listed as not synced: " + + unclassified + ". If the STOP-TB app reads it, add it to a StopTBSyncType's sourceTables and add " + + "change-log triggers in a new AMRIT-DB migration; otherwise list it here with a reason."); + } + + @Test + void syncedTablesAreNotAlsoListedAsNotSynced() { + for (StopTBSyncType t : StopTBSyncType.values()) { + for (String table : t.sourceTables()) { + assertTrue(!NOT_SYNCED_STOPTB.containsKey(table) && !OTHER_PROGRAMMES.contains(table), table); + } + } + } + + /** schema.table for every @Entity under com.iemr.flw.domain, lower-case. */ + static Set mappedTables() throws ClassNotFoundException { + ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); + scanner.addIncludeFilter(new AnnotationTypeFilter(Entity.class)); + Set tables = new TreeSet<>(); + for (BeanDefinition bd : scanner.findCandidateComponents("com.iemr.flw.domain")) { + Class c = Class.forName(bd.getBeanClassName()); + Table t = c.getAnnotation(Table.class); + String name = t != null && !t.name().isEmpty() ? t.name() : c.getSimpleName(); + String schema = t != null && !t.schema().isEmpty() ? t.schema() + : c.getPackageName().endsWith(".identity") ? "db_identity" : "db_iemr"; + tables.add((schema + "." + name).toLowerCase(Locale.ROOT)); + } + return tables; + } +} diff --git a/src/test/java/com/iemr/flw/service/sync/StopTBSyncTypeTest.java b/src/test/java/com/iemr/flw/service/sync/StopTBSyncTypeTest.java new file mode 100644 index 000000000..a7fa90e80 --- /dev/null +++ b/src/test/java/com/iemr/flw/service/sync/StopTBSyncTypeTest.java @@ -0,0 +1,21 @@ +package com.iemr.flw.service.sync; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class StopTBSyncTypeTest { + + @Test + void childKeyQueriesTargetTheGivenBeneficiariesWithoutPaging() { + for (StopTBSyncType t : StopTBSyncType.values()) { + if (t == StopTBSyncType.BENEFICIARY) continue; + String sql = t.keysForBeneficiariesSql(); + assertTrue(sql.contains(":regIds"), t + ": " + sql); + assertFalse(sql.contains(":after"), t + ": " + sql); + assertFalse(sql.contains(":limit"), t + ": " + sql); + assertFalse(sql.contains(":villages"), t + ": " + sql); + System.out.println(t + " | " + sql); + } + } +} diff --git a/src/test/java/com/iemr/flw/service/sync/SyncTokenTest.java b/src/test/java/com/iemr/flw/service/sync/SyncTokenTest.java new file mode 100644 index 000000000..3f247605b --- /dev/null +++ b/src/test/java/com/iemr/flw/service/sync/SyncTokenTest.java @@ -0,0 +1,51 @@ +package com.iemr.flw.service.sync; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SyncTokenTest { + + private static final String UUID = "3f2a6c1e-1b2c-11ef-9f1a-0242ac120002"; + + @Test + void deltaRoundTrips() { + SyncToken t = SyncToken.parse(SyncToken.delta(UUID, 1000, 1761700000L, 0b101, 42L).encode()); + assertEquals(SyncToken.Kind.DELTA, t.kind()); + assertEquals(UUID, t.serverId()); + assertEquals(1000, t.seq()); + assertEquals(1761700000L, t.issuedAt()); + assertEquals(0b101, t.typesMask()); + assertEquals(42L, t.scopeHash()); + } + + @Test + void snapshotRoundTrips() { + SyncToken t = SyncToken.parse(SyncToken.snapshot(UUID, 5230, 3, 900, 7, 99L).encode()); + assertEquals(SyncToken.Kind.SNAPSHOT, t.kind()); + assertEquals(5230, t.seq()); + assertEquals(3, t.typeIndex()); + assertEquals(900, t.afterKey()); + assertEquals(7, t.typesMask()); + assertEquals(99L, t.scopeHash()); + } + + @Test + void unreadableTokensMeanStartOver() { + assertNull(SyncToken.parse(null)); + assertNull(SyncToken.parse("")); + assertNull(SyncToken.parse("garbage")); + assertNull(SyncToken.parse("d." + UUID + ".x.1.1.1")); + assertNull(SyncToken.parse("d." + UUID + ".1.1.1")); // no scope: issued before scope was bound + assertNull(SyncToken.parse("d..1.1.1.1")); + assertNull(SyncToken.parse("q." + UUID + ".1.1")); + } + + @Test + void everyLogEntityMapsToOneType() { + for (StopTBSyncType t : StopTBSyncType.values()) { + assertSame(t, StopTBSyncType.fromLogEntity(t.logEntity())); + } + assertNull(StopTBSyncType.fromLogEntity("unknown")); + } +} diff --git a/src/test/java/com/iemr/flw/service/sync/UserSyncScopeTest.java b/src/test/java/com/iemr/flw/service/sync/UserSyncScopeTest.java new file mode 100644 index 000000000..1398bbbdf --- /dev/null +++ b/src/test/java/com/iemr/flw/service/sync/UserSyncScopeTest.java @@ -0,0 +1,42 @@ +package com.iemr.flw.service.sync; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class UserSyncScopeTest { + + // Two mapping rows for service 1738 (villages merge), one for 1744, and a malformed entry. + private final UserSyncScope scope = UserSyncScope.fromMappingRows(List.of( + new Object[]{1738, "5443, 5444"}, + new Object[]{1738, "5445,x"}, + new Object[]{1744, "6677"}, + new Object[]{1750, null})); + + @Test + void noVillagesAskedForMeansAllOfTheUsersVillagesForThatService() { + assertEquals(List.of(5443, 5444, 5445), scope.resolve(1738, List.of())); + assertEquals(List.of(6677), scope.resolve(1744, null)); + } + + @Test + void askedForVillagesMustAllBeAssigned() { + assertEquals(List.of(5443, 5445), scope.resolve(1738, List.of(5445, 5443, 5445))); + assertThrows(IllegalArgumentException.class, () -> scope.resolve(1738, List.of(5443, 6677))); + } + + @Test + void unmappedServiceOrServiceWithoutVillagesIsRejected() { + assertThrows(IllegalArgumentException.class, () -> scope.resolve(9999, List.of())); + assertThrows(IllegalArgumentException.class, () -> scope.resolve(1750, List.of())); + } + + @Test + void scopeHashIgnoresVillageOrderButNotContent() { + assertEquals(UserSyncScope.scopeHash(1738, List.of(5443, 5444)), UserSyncScope.scopeHash(1738, List.of(5444, 5443))); + assertNotEquals(UserSyncScope.scopeHash(1738, List.of(5443)), UserSyncScope.scopeHash(1738, List.of(5443, 5444))); + assertNotEquals(UserSyncScope.scopeHash(1738, List.of(5443)), UserSyncScope.scopeHash(1744, List.of(5443))); + } +}