Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# CLAUDE.md - HWC-API

## Project Overview

HWC-API (Health and Wellness Centre API) is the backend service for Health and Wellness Centres in the AMRIT platform. It handles patient registration, nurse assessments, doctor consultations, lab tests, prescriptions, teleconsultation, and various clinical workflows including ANC, PNC, NCD screening, cancer screening, neonatal/childhood care, family planning, COVID-19, and general OPD. It also supports offline data sync for field-level workers and CHO (Community Health Officer) app sync.

## Tech Stack

- Java 17, Spring Boot 3.2.2, Maven
- Spring Data JPA / Hibernate, MySQL 8.0
- Redis for session management
- Lombok, MapStruct
- SpringDoc OpenAPI (Swagger UI at `/swagger-ui.html`)
- ECS logging (logback-ecs-encoder)
- JaCoCo for test coverage
- Packaged as WAR for Wildfly deployment

## Build & Run

```bash
mvn clean install -DENV_VAR=local # Build
mvn spring-boot:run -DENV_VAR=local # Run locally
mvn -B package --file pom.xml -P <profile> # Package WAR (dev, local, test, ci, uat)
mvn test # Run tests
```

Environment config: `src/main/resources/common_<ENV_VAR>.properties` is copied to `application.properties` at build time.

## Key Packages (`com.iemr.hwc`)

- **controller/** - REST endpoints organized by clinical workflow:
- `registrar/` - Patient registration (main + master data)
- `anc/` - Antenatal Care
- `pnc/` - Postnatal Care
- `ncdscreening/` - NCD Screening (diabetes, hypertension, oral, breast, cervical cancer)
- `ncdCare/` - NCD Care follow-up
- `cancerscreening/` - Cancer screening workflows
- `generalOPD/` - General OPD consultations
- `quickconsult/` - Quick consultation
- `neonatal/` - Neonatal and immunization services
- `adolescent/` - Childhood and adolescent health
- `familyPlanning/` - Family planning services
- `covid19/` - COVID-19 screening and management
- `labtechnician/` - Lab test ordering and results
- `teleconsultation/` - Telemedicine specialist consultations
- `videoconsultation/` - Video consultation (QuickBlox integration)
- `foetalmonitor/` - Foetal monitor integration
- `dataSyncActivity/` - Offline data sync (van/spoke to server)
- `dataSyncLayerCentral/` - Central data sync layer
- `choApp/` - CHO App sync endpoints
- `patientApp/` - Patient mobile app endpoints
- `common/` - Shared endpoints (worklist, master data)
- `location/` - Location management
- `masterVillage/` - Village master data
- `report/` - CRM reports
- `snomedct/` - SNOMED CT terminology
- `spoke/` - Van/spoke management
- `uptsu/` - UP Technical Support Unit integration
- `diabetesAndHypertensionOutcome/` - Diabetes/hypertension outcome tracking
- `wo/` - Location controller (alternate)
- **service/** - Business logic layer (mirrors controller structure)
- **repo/** - Spring Data JPA repositories
- **data/** - JPA entities organized by domain (doctor, nurse, anc, pnc, neonatal, labModule, etc.)
- **annotation/** - Custom annotations (SQL injection safe validation)
- **config/** - Application configuration
- **utils/** - Utilities (Redis, HTTP, validation, session, gateway, email, exception handling, mapper)

## Architecture Notes

- Comprehensive clinical workflow engine supporting 10+ visit types (ANC, PNC, NCD, cancer, general OPD, neonatal, adolescent, family planning, COVID-19, quick consult)
- Beneficiary flow status (`benFlowStatus/`) tracks patients through registration -> nurse -> doctor -> lab -> pharmacist pipeline
- Offline-first architecture: data sync layer enables van/spoke devices to work offline and sync to central server
- CHO App sync provides mobile-optimized endpoints for Community Health Officers
- Teleconsultation integration enables specialist remote consultations
- Video consultation via QuickBlox
- Foetal monitor device integration for ANC
- UPTSU integration for Uttar Pradesh government health system
- SNOMED CT for standardized clinical terminology
- SQL injection prevention via custom annotation validators
- Artifact ID: `hwc-api`, group: `com.iemr.hwc`, version: 3.2.1
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.iemr.hwc</groupId>
<artifactId>hwc-api</artifactId>
<version>3.8.0</version>
<version>3.9.0</version>
<packaging>war</packaging>

<name>HWC-API</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ public void setCommonNurseServiceImpl(CommonNurseServiceImpl commonNurseServiceI
}

@Operation(summary = "Get doctor worklist")
@GetMapping(value = { "/getDocWorklistNew/{providerServiceMapID}/{serviceID}/{vanID}" })
@GetMapping(value = { "/getDocWorklistNew/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getDocWorkListNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("serviceID") Integer serviceID, @PathVariable("vanID") Integer vanID) {
@PathVariable("serviceID") Integer serviceID, @PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
if (providerServiceMapID != null && serviceID != null) {
String s = commonDoctorServiceImpl.getDocWorkListNew(providerServiceMapID, serviceID, vanID);
String s = commonDoctorServiceImpl.getDocWorkListNew(providerServiceMapID, serviceID, facilityID);
if (s != null)
response.setResponse(s);
} else {
Expand All @@ -96,15 +96,15 @@ public String getDocWorkListNew(@PathVariable("providerServiceMapID") Integer pr

// doc worklist new (TM future scheduled beneficiary)
@Operation(summary = "Get doctor future worklist scheduled for telemedicine")
@GetMapping(value = { "/getDocWorkListNewFutureScheduledForTM/{providerServiceMapID}/{serviceID}/{vanID}" })
@GetMapping(value = { "/getDocWorkListNewFutureScheduledForTM/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getDocWorkListNewFutureScheduledForTM(
@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("serviceID") Integer serviceID, @PathVariable("vanID") Integer vanID) {
@PathVariable("serviceID") Integer serviceID, @PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
if (providerServiceMapID != null && serviceID != null) {
String s = commonDoctorServiceImpl.getDocWorkListNewFutureScheduledForTM(providerServiceMapID,
serviceID, vanID);
serviceID, facilityID);
if (s != null)
response.setResponse(s);
} else {
Expand All @@ -122,12 +122,12 @@ public String getDocWorkListNewFutureScheduledForTM(

// nurse worklist new
@Operation(summary = "Get nurse worklist")
@GetMapping(value = { "/getNurseWorklistNew/{providerServiceMapID}/{serviceID}/{vanID}" })
@GetMapping(value = { "/getNurseWorklistNew/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getNurseWorkListNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("vanID") Integer vanID) {
@PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
String s = commonNurseServiceImpl.getNurseWorkListNew(providerServiceMapID, vanID);
String s = commonNurseServiceImpl.getNurseWorkListNew(providerServiceMapID, facilityID);
if (s != null)
response.setResponse(s);
else
Expand All @@ -142,12 +142,12 @@ public String getNurseWorkListNew(@PathVariable("providerServiceMapID") Integer

// nurse worklist TC schedule (current-date) new
@Operation(summary = "Get worklist for teleconsultation for the current date")
@GetMapping(value = { "/getNurseWorkListTcCurrentDate/{providerServiceMapID}/{serviceID}/{vanID}" })
@GetMapping(value = { "/getNurseWorkListTcCurrentDate/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getNurseWorkListTcCurrentDateNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("vanID") Integer vanID) {
@PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
String s = commonNurseServiceImpl.getNurseWorkListTcCurrentDate(providerServiceMapID, vanID);
String s = commonNurseServiceImpl.getNurseWorkListTcCurrentDate(providerServiceMapID, facilityID);
if (s != null)
response.setResponse(s);
else
Expand All @@ -161,12 +161,12 @@ public String getNurseWorkListTcCurrentDateNew(@PathVariable("providerServiceMap

// nurse worklist TC schedule (future-date) new
@Operation(summary = "Get worklist for teleconsultation for the future date")
@GetMapping(value = { "/getNurseWorkListTcFutureDate/{providerServiceMapID}/{serviceID}/{vanID}" })
@GetMapping(value = { "/getNurseWorkListTcFutureDate/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getNurseWorkListTcFutureDateNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("vanID") Integer vanID) {
@PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
String s = commonNurseServiceImpl.getNurseWorkListTcFutureDate(providerServiceMapID, vanID);
String s = commonNurseServiceImpl.getNurseWorkListTcFutureDate(providerServiceMapID, facilityID);
if (s != null)
response.setResponse(s);
else
Expand Down Expand Up @@ -204,12 +204,12 @@ public String getDoctorPreviousSignificantFindings(

// Get Lab technician worklist new
@Operation(summary = "Get lab technician worklist")
@GetMapping(value = { "/getLabWorklistNew/{providerServiceMapID}/{serviceID}/{vanID}" })
@GetMapping(value = { "/getLabWorklistNew/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getLabWorkListNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("vanID") Integer vanID) {
@PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
String s = commonNurseServiceImpl.getLabWorkListNew(providerServiceMapID, vanID);
String s = commonNurseServiceImpl.getLabWorkListNew(providerServiceMapID, facilityID);
if (s != null)
response.setResponse(s);
else
Expand All @@ -223,12 +223,12 @@ public String getLabWorkListNew(@PathVariable("providerServiceMapID") Integer pr

// Get radiologist worklist new
@Operation(summary = "Get radiologist worklist")
@PostMapping(value = { "/getRadiologist-worklist-New/{providerServiceMapID}/{serviceID}/{vanID}" })
@PostMapping(value = { "/getRadiologist-worklist-New/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getRadiologistWorklistNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("vanID") Integer vanID) {
@PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
String s = commonNurseServiceImpl.getRadiologistWorkListNew(providerServiceMapID, vanID);
String s = commonNurseServiceImpl.getRadiologistWorkListNew(providerServiceMapID, facilityID);
if (s != null)
response.setResponse(s);
else
Expand All @@ -242,12 +242,12 @@ public String getRadiologistWorklistNew(@PathVariable("providerServiceMapID") In

// Get oncologist worklist new
@Operation(summary = "Get oncologist worklist")
@PostMapping(value = { "/getOncologist-worklist-New/{providerServiceMapID}/{serviceID}/{vanID}" })
@PostMapping(value = { "/getOncologist-worklist-New/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getOncologistWorklistNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("vanID") Integer vanID) {
@PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
String s = commonNurseServiceImpl.getOncologistWorkListNew(providerServiceMapID, vanID);
String s = commonNurseServiceImpl.getOncologistWorkListNew(providerServiceMapID, facilityID);
if (s != null)
response.setResponse(s);
else
Expand All @@ -261,12 +261,12 @@ public String getOncologistWorklistNew(@PathVariable("providerServiceMapID") Int

// Get pharma worklist new
@Operation(summary = "Get pharmacist worklist")
@GetMapping(value = { "/getPharma-worklist-New/{providerServiceMapID}/{serviceID}/{vanID}" })
@GetMapping(value = { "/getPharma-worklist-New/{providerServiceMapID}/{serviceID}/{facilityID}" })
public String getPharmaWorklistNew(@PathVariable("providerServiceMapID") Integer providerServiceMapID,
@PathVariable("vanID") Integer vanID) {
@PathVariable("facilityID") Integer facilityID) {
OutputResponse response = new OutputResponse();
try {
String s = commonNurseServiceImpl.getPharmaWorkListNew(providerServiceMapID, vanID);
String s = commonNurseServiceImpl.getPharmaWorkListNew(providerServiceMapID, facilityID);
if (s != null)
response.setResponse(s);
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,9 @@
OutputResponse response = new OutputResponse();
try {
JSONObject obj = new JSONObject(comingRequest);
if (obj != null && obj.has("vanID") && obj.has("spPSMID") && obj.get("vanID") != null
&& obj.get("spPSMID") != null) {
String s = locationServiceImpl.getLocDetailsNew(obj.getInt("vanID"), obj.getInt("spPSMID"), obj);

if (obj != null && obj.has("facilityID") && obj.get("facilityID") != null

Check failure on line 187 in src/main/java/com/iemr/hwc/controller/location/LocationController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "facilityID" 4 times.

See more on https://sonarcloud.io/project/issues?id=PSMRI_HWC-API&issues=AZ-OceQVyYxhXOQCMfpn&open=AZ-OceQVyYxhXOQCMfpn&pullRequest=219
&& !obj.isNull("facilityID") && obj.has("spPSMID") && obj.get("spPSMID") != null) {

Check failure on line 188 in src/main/java/com/iemr/hwc/controller/location/LocationController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "spPSMID" 3 times.

See more on https://sonarcloud.io/project/issues?id=PSMRI_HWC-API&issues=AZ-OceQVyYxhXOQCMfpo&open=AZ-OceQVyYxhXOQCMfpo&pullRequest=219
String s = locationServiceImpl.getLocDetailsByFacilityID(obj.getInt("facilityID"), obj.getInt("spPSMID"));
response.setResponse(s);
} else {
response.setError(5000, "Invalid request");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,23 @@ public String getServicepointVillages(@RequestBody String comingRequest) {
return response.toString();
}

@Operation(summary = "Get user van details")
@PostMapping(value = "/getUserVanSpDetails", produces = { "application/json" })
public String getUserVanSpDetails(@RequestBody String comingRequest) {
@Operation(summary = "Get user facility details")
@PostMapping(value = "/getUserFacilityDetails", produces = { "application/json" })
public String getUserFacilityDetails(@RequestBody String comingRequest) {
OutputResponse response = new OutputResponse();
try {

JSONObject obj = new JSONObject(comingRequest);
logger.info("getServicepointVillages request " + comingRequest);
if (obj.has("userID") && obj.has("providerServiceMapID")) {
String responseData = iemrMmuLoginServiceImpl.getUserVanSpDetails(obj.getInt("userID"),
String responseData = iemrMmuLoginServiceImpl.getUserFacilityOnlyDetails(obj.getInt("userID"),
obj.getInt("providerServiceMapID"));
response.setResponse(responseData);
} else {
response.setError(5000, "Invalid request");
}
} catch (Exception e) {
response.setError(5000, "Error while getting van and service points data");
response.setError(5000, e.getMessage() != null ? e.getMessage() : "Error while getting van and service points data");
logger.error("getUserVanSpDetails failed with " + e.getMessage(), e);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ public String getActionMaster() {
}

@Operation(summary = "Fetch 104 work list data")
@GetMapping("/getWorklistByVanID/{vanId}")
public String getWolklist104Data(@PathVariable Integer vanId) {
@GetMapping({"/getWorklistByFacilityID/{facilityID}", "/getWorklistByVanID/{facilityID}"})
public String getWolklist104Data(@PathVariable Integer facilityID) {
OutputResponse response = new OutputResponse();
String resp = null;
logger.info("Entered into getWorklist method with vanId : " + vanId);
logger.info("Entered into getWorklist method with facilityID : " + facilityID);
try {
resp = uptsuService.getWorklist(vanId);
resp = uptsuService.getWorklistByFacilityID(facilityID);
if (null != resp) {
response.setResponse(resp.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ public class BeneficiaryFlowStatus {
@Column(name = "vanID")
private Integer vanID;

@Expose
@Column(name = "facilityID")
private Integer facilityID;

@Expose
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "vanID", referencedColumnName = "vanID", insertable = false, updatable = false)
Expand Down Expand Up @@ -514,6 +518,14 @@ public void setVanID(Integer vanID) {
this.vanID = vanID;
}

public Integer getFacilityID() {
return facilityID;
}

public void setFacilityID(Integer facilityID) {
this.facilityID = facilityID;
}

public String getVanNo() {
return vanNo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ public class BeneficiaryVisitDetail {
@Column(name = "VisitFlowStatusFlag", insertable = false)
private @SQLInjectionSafe String visitFlowStatusFlag;

@Expose
@Column(name = "NurseID")
private Long nurseID;

@Expose
@Column(name = "DoctorID")
private Long doctorID;

@Expose
@Column(name = "PharmacistID")
private Long pharmacistID;

@Expose
@Column(name = "LabTechnicianID")
private Long labTechnicianID;

@Expose
@Column(name = "VanSerialNo")
private Long vanSerialNo;
Expand Down Expand Up @@ -246,7 +262,10 @@ public class BeneficiaryVisitDetail {

@Transient
private String informationGiven;


@Transient
private Integer facilityID;


public BeneficiaryVisitDetail() {
super();
Expand Down
Loading