diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index cb148d02..5f6fda30 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -59,10 +59,14 @@ start-email-scheduler=true cron-scheduler-email=0 0/1 * * * ? * ###cti data check with call detail report Scheduler -#Runs at everyday 12:10AM -start-ctidatacheck-scheduler=true -cron-scheduler-ctidatacheck=0 10 00 * * * - +start-ctidatacheck-scheduler=@env.START_CTIDATACHECK_SCHEDULER@ +cron-scheduler-ctidatacheck=0 00 02 * * * +##-------------------------------###cti data sync Scheduler configurations------------------------------------------------------ +start-ctidatasync-scheduler=@env.START_CTIDATASYNC_SCHEDULER@ +cron-scheduler-ctidatasync=0 30 01 * * ? * +##-----------------------------------------------#NHM data dashboard schedular---------------------------------------------------------------- +start-nhmdashboard-scheduler=@env.START_NHMDASHBOARD_SCHEDULER@ +cron-scheduler-nhmdashboard=0 30 0 * * ? * ### generate Beneficiary Config genben-api= @env.BEN_GEN_API_URL@ @@ -175,7 +179,7 @@ springdoc.swagger-ui.enabled=false isProduction=@env.IS_PRODUCTION@ grievanceAllocationRetryConfiguration=3 -start-grievancedatasync-scheduler=true +start-grievancedatasync-scheduler=@env.START_GRIEVANCEDATASYNC_SCHEDULER@ cron-scheduler-grievancedatasync=0 0/2 * * * ? captcha.secret-key=@env.CAPTCHA_SECRET_KEY@ diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 817cf696..240aa80c 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -55,9 +55,14 @@ cron-scheduler-email=0 0/1 * * * ? * ###cti data check with call detail report Scheduler #Runs at everyday 12:10AM -start-ctidatacheck-scheduler=true -cron-scheduler-ctidatacheck=0 10 00 * * * - +start-ctidatacheck-scheduler=${START_CTIDATACHECK_SCHEDULER} +cron-scheduler-ctidatacheck=0 00 02 * * * +##-------------------------------###cti data sync Scheduler configurations------------------------------------------------------ +start-ctidatasync-scheduler=${START_CTIDATASYNC_SCHEDULER} +cron-scheduler-ctidatasync=0 30 01 * * ? * +##-----------------------------------------------#NHM data dashboard schedular---------------------------------------------------------------- +start-nhmdashboard-scheduler=${START_NHMDASHBOARD_SCHEDULER} +cron-scheduler-nhmdashboard=0 30 0 * * ? * ### generate Beneficiary Config genben-api= ${BEN_GEN_API_URL} @@ -171,7 +176,7 @@ springdoc.swagger-ui.enabled=false isProduction=${IS_PRODUCTION} grievanceAllocationRetryConfiguration=3 -start-grievancedatasync-scheduler=false +start-grievancedatasync-scheduler=${START_GRIEVANCEDATASYNC_SCHEDULER} cron-scheduler-grievancedatasync=0 0/2 * * * ? captcha.secret-key=${CAPTCHA_SECRET_KEY} diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index 7bc96104..09a526dd 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -64,13 +64,13 @@ cron-scheduler-email=0 0/1 * * * ? * ##-------------------------------###cti data sync Scheduler configurations------------------------------------------------------ start-ctidatasync-scheduler=false -cron-scheduler-ctidatasync=0 0 2 */2 * ? +cron-scheduler-ctidatasync=0 30 01 * * ? * ##-------------------------------###cti data check with call detail report Scheduler------------------------------------------------------ #Runs at everyday 12:10AM -start-ctidatacheck-scheduler=true -cron-scheduler-ctidatacheck=0 10 00 * * * +start-ctidatacheck-scheduler=false +cron-scheduler-ctidatacheck=0 00 02 * * * ##---------------------------------#### Registration schedular for Avni------------------------------------------------------------------------------ @@ -198,7 +198,7 @@ grievanceAllocationRetryConfiguration=3 logging.path=logs/ logging.file.name=logs/common-api.log -video-call-url=https://vc.piramalswasthya.org/ +video-call-url=https://vc.piramalswasthya.org/? jibri.output.path=/srv/jibri/recordings video.recording.path=/srv/recordings diff --git a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java index 9a924872..065cdb14 100644 --- a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java +++ b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java @@ -1,31 +1,97 @@ package com.iemr.common.controller.dynamicForm; -import com.iemr.common.data.dynamic_from.FormEntity; -import com.iemr.common.data.dynamic_from.ModuleEntity; -import com.iemr.common.dto.dynamicForm.FormTypeEntityDTO; -import com.iemr.common.dto.dynamicForm.ModuleEntityDTO; -import com.iemr.common.service.dynamicForm.FormTypeService; -import com.iemr.common.service.dynamicForm.ModuleService; +import com.iemr.common.dto.dynamicForm.FieldDTO; +import com.iemr.common.dto.dynamicForm.FormDTO; +import com.iemr.common.dto.dynamicForm.ModuleDTO; +import com.iemr.common.service.dynamicForm.FormMasterService; +import com.iemr.common.utils.response.ApiResponse; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -@RequestMapping(value = "masterFrom") +import java.util.List; + +@RequestMapping(value = "dynamicForm",headers = "Authorization") @RestController public class DynamicFormController { - @Autowired ModuleService moduleService; @Autowired - private FormTypeService formTypeService; + private FormMasterService formMasterService; + + @PostMapping(value = "createModule",headers = "Authorization") + public ResponseEntity> createModule(@Valid @RequestBody ModuleDTO moduleDTO) { + try { + Object result = formMasterService.createModule(moduleDTO); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success("Module created successfully", HttpStatus.OK.value(), result)); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(ApiResponse.error("Invalid module data: " + e.getMessage(), HttpStatus.BAD_REQUEST.value(), null)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error("Failed to create module", HttpStatus.INTERNAL_SERVER_ERROR.value(), null)); + } + } + + @PostMapping(value = "createForm",headers = "Authorization") + public ResponseEntity> createForm(@Valid @RequestBody FormDTO dto) { + try { + Object result = formMasterService.createForm(dto); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success("Form created successfully", HttpStatus.OK.value(), result)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error("Failed to create form", HttpStatus.INTERNAL_SERVER_ERROR.value(), null)); + } + } + + @PostMapping(value = "createFields",headers = "Authorization") + public ResponseEntity> createField(@Valid @RequestBody List dto) { + try { + Object result = formMasterService.createField(dto); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success("Fields created successfully", HttpStatus.OK.value(), result)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error("Failed to create fields", HttpStatus.INTERNAL_SERVER_ERROR.value(), null)); + } + } - @PostMapping("/modules") - public ResponseEntity createModule(@RequestBody ModuleEntityDTO module) { - return ResponseEntity.ok(moduleService.save(module)); + @PostMapping(value = "field/update",headers = "Authorization") + public ResponseEntity> updateField(@Valid @RequestBody FieldDTO dto) { + try { + Object result = formMasterService.updateField(dto); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success("Field updated successfully", HttpStatus.OK.value(), result)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error("Failed to update field", HttpStatus.INTERNAL_SERVER_ERROR.value(), null)); + } } - @PostMapping("/modules/{moduleId}/form-types") - public ResponseEntity createFormType(@PathVariable Long moduleId, @RequestBody FormTypeEntityDTO formType) { - return ResponseEntity.ok(formTypeService.createFormType(moduleId, formType)); + @DeleteMapping(value = "delete/{fieldId}/field",headers = "Authorization") + public ResponseEntity> deleteField(@PathVariable Long fieldId) { + try { + formMasterService.deleteField(fieldId); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success("Field deleted successfully", HttpStatus.OK.value(), null)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error("Failed to delete field", HttpStatus.INTERNAL_SERVER_ERROR.value(), null)); + } } + @GetMapping(value = "form/{formId}/fields",headers = "Authorization") + public ResponseEntity> getStructuredForm(@PathVariable String formId) { + try { + Object result = formMasterService.getStructuredFormByFormId(formId); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success("Form structure fetched successfully", HttpStatus.OK.value(), result)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error("Failed to fetch form structure", HttpStatus.INTERNAL_SERVER_ERROR.value(), null)); + } + } } diff --git a/src/main/java/com/iemr/common/controller/sms/SMSController.java b/src/main/java/com/iemr/common/controller/sms/SMSController.java index 78a42793..ee985947 100644 --- a/src/main/java/com/iemr/common/controller/sms/SMSController.java +++ b/src/main/java/com/iemr/common/controller/sms/SMSController.java @@ -207,7 +207,13 @@ public String sendSMS( } logger.debug("sendSMS sending response " + response); logger.info("sendSMS sending response"); - return response.toString(); + String respStr = response.toString(); + respStr = respStr.replace("\\u003d", "=") + .replace("\\u003c", "<") + .replace("\\u003e", ">") + .replace("\\u0026", "&"); + return respStr; + } } diff --git a/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java b/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java index 4a1af2a2..e1b20d66 100644 --- a/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java +++ b/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java @@ -156,7 +156,7 @@ public String userAuthenticate( logger.info("CAPTCHA validated successfully for user: {}", m_User.getUserName()); } else { logger.warn("CAPTCHA token missing for user: {}", m_User.getUserName()); - response.setError(new IEMRException("CAPTCHA token is required")); + response.setError(new IEMRException("CAPTCHA validation failed. Please try again.")); return response.toString(); } } else { @@ -254,20 +254,24 @@ public ResponseEntity refreshToken(@RequestBody Map request) try { if (jwtUtil.validateToken(refreshToken) == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token"); + logger.warn("Token validation failed: invalid token provided."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized."); } Claims claims = jwtUtil.getAllClaimsFromToken(refreshToken); // Verify token type if (!"refresh".equals(claims.get("token_type", String.class))) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token type"); + logger.warn("Token validation failed: incorrect token type in refresh request."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized."); + } // Check revocation using JTI String jti = claims.getId(); if (!redisTemplate.hasKey("refresh:" + jti)) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Token revoked"); + logger.warn("Token validation failed: refresh token is revoked or not found in store."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized."); } // Get user details @@ -277,11 +281,13 @@ public ResponseEntity refreshToken(@RequestBody Map request) // Validate that the user still exists and is active if (user == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User not found"); + logger.warn("Token validation failed: user not found for userId in token."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized."); } if (user.getM_status() == null || !"Active".equalsIgnoreCase(user.getM_status().getStatus())) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User account is inactive"); + logger.warn("Token validation failed: user account is inactive or not in 'Active' status."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized."); } // Generate new tokens String newJwt = jwtUtil.generateToken(user.getUserName(), userId); @@ -302,10 +308,14 @@ public ResponseEntity refreshToken(@RequestBody Map request) return ResponseEntity.ok(tokens); } catch (ExpiredJwtException ex) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Token expired"); + logger.warn("Token validation failed: token has expired."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body("Authentication failed. Please log in again."); } catch (Exception e) { logger.error("Refresh failed: ", e); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Token refresh failed"); + logger.error("Token refresh failed due to unexpected server error."); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("An unexpected error occurred. Please try again later."); } } @@ -321,10 +331,12 @@ public String logOutUserFromConcurrentSession( List mUsers = iemrAdminUserServiceImpl.userExitsCheck(m_User.getUserName()); if (mUsers == null || mUsers.size() <= 0) { - throw new IEMRException("User not found, please contact administrator"); - } else if (mUsers.size() > 1) - throw new IEMRException("More than 1 user found, please contact administrator"); - else if (mUsers.size() == 1) { + logger.error("User not found"); + throw new IEMRException("Logout request failed, please try again later"); + } else if (mUsers.size() > 1) { + logger.error("More than 1 user found"); + throw new IEMRException("Logout failed. Please retry or contact administrator"); + } else if (mUsers.size() == 1) { String previousTokenFromRedis = sessionObject .getSessionObject((mUsers.get(0).getUserName().toString().trim().toLowerCase())); if (previousTokenFromRedis != null) { @@ -332,7 +344,8 @@ else if (mUsers.size() == 1) { sessionObject.deleteSessionObject(previousTokenFromRedis); response.setResponse("User successfully logged out"); } else - throw new IEMRException("Unable to fetch session from redis"); + logger.error("Unable to fetch session from redis"); + throw new IEMRException("Session error. Please try again later"); } } else { throw new IEMRException("Invalid request object"); @@ -404,7 +417,7 @@ private void createUserMapping(User mUser, JSONObject resMap, JSONObject service previlegeObj.getJSONObject(serv).put("agentPassword", m_UserServiceRoleMapping.getAgentPassword()); } JSONArray roles = previlegeObj.getJSONObject(serv).getJSONArray("roles"); -// roles.put(new JSONObject(m_UserServiceRoleMapping.getM_Role().toString())); + // roles.put(new JSONObject(m_UserServiceRoleMapping.getM_Role().toString())); JSONObject roleObject = new JSONObject(m_UserServiceRoleMapping.getM_Role().toString()); roleObject.put("teleConsultation", m_UserServiceRoleMapping.getTeleConsultation()); roles.put(roleObject); @@ -563,7 +576,8 @@ public String getLoginResponse(HttpServletRequest request) { } if (jwtToken == null) { - throw new IEMRException("No authentication token found in header or cookie"); + logger.warn("Authentication failed: no token found in header or cookies."); + throw new IEMRException("Authentication failed. Please log in again."); } // Extract user ID from the JWT token @@ -572,7 +586,9 @@ public String getLoginResponse(HttpServletRequest request) { // Get user details and prepare response User user = iemrAdminUserServiceImpl.getUserById(Long.parseLong(userId)); if (user == null) { - throw new IEMRException("User not found"); + logger.warn("User lookup failed for provided userId."); + throw new IEMRException("Authentication failed. Please try again."); + } String remoteAddress = request.getHeader("X-FORWARDED-FOR"); @@ -603,10 +619,13 @@ public String forgetPassword( List mUsers = iemrAdminUserServiceImpl.userExitsCheck(m_User.getUserName()); if (mUsers == null || mUsers.size() <= 0) { - throw new IEMRException("user not found, please contact administrator"); - } else if (mUsers.size() > 1) - throw new IEMRException("more than 1 user found, please contact administrator"); - else if (mUsers.size() == 1) { + logger.error("User not found"); + throw new IEMRException("If the username is registered, you will be asked a security question"); + } else if (mUsers.size() > 1) { + logger.error("More than 1 user found"); + throw new IEMRException("If the username is registered, you will be asked a security question"); + + } else if (mUsers.size() == 1) { List> quesAnsList = new ArrayList<>(); Map quesAnsMap; Map resMap = new HashMap<>(); @@ -642,8 +661,11 @@ public String setPassword( int noOfRowModified = 0; List mUsers = iemrAdminUserServiceImpl.userExitsCheck(m_user.getUserName()); if (mUsers.size() != 1) { - throw new IEMRException( - "Set forgot password failed as the user does not exist or is not active or multiple user found.Please contact with administrator"); + logger.warn( + "Password reset failed for username '{}'. Reason: user not found, inactive, or multiple matches.", + m_user.getUserName()); + + throw new IEMRException("Unable to process your request. Please try again or contact support."); } User mUser = mUsers.get(0); String setStatus; @@ -660,7 +682,7 @@ public String setPassword( } catch (Exception e) { logger.error("setForgetPassword failed with error " + e.getMessage(), e); if (e.getMessage().equals( - "Set forgot password failed as the user does not exist or is not active or multiple user found.Please contact with administrator")) + "Unable to process your request. Please try again or contact support.")) response.setError(e); else response.setError(5000, e.getMessage()); @@ -681,7 +703,9 @@ public String changePassword( List mUsers = iemrAdminUserServiceImpl.userExitsCheck(changePassword.getUserName()); String changeReqResult; if (mUsers.size() != 1) { - throw new IEMRException("Change password failed with error as user is not available"); + logger.warn("Change password attempt failed. User not found or not available."); + + throw new IEMRException("Unable to change password. Please try again later"); } try { int validatePassword; @@ -748,7 +772,7 @@ public String getSecurityts() { response.setResponse(test.toString()); } catch (Exception e) { logger.error("getsecurityquetions failed with error " + e.getMessage(), e); - response.setError(e); + response.setError(5000, "Unable to fetch security questions"); } logger.info("getsecurityquetions response " + response.toString()); return response.toString(); @@ -1079,8 +1103,8 @@ public String validateSecurityQuestionAndAnswer( } else throw new IEMRException("Invalid Request"); } catch (Exception e) { + logger.error("validateSecurityQuestionAndAnswer failed: {}", e.toString()); response.setError(5000, e.getMessage()); - logger.error(e.toString()); } logger.info("validateSecurityQuestionAndAnswer API response" + response.toString()); return response.toString(); diff --git a/src/main/java/com/iemr/common/data/dynamic_from/FormDefinition.java b/src/main/java/com/iemr/common/data/dynamic_from/FormDefinition.java new file mode 100644 index 00000000..9e62b6d9 --- /dev/null +++ b/src/main/java/com/iemr/common/data/dynamic_from/FormDefinition.java @@ -0,0 +1,30 @@ +package com.iemr.common.data.dynamic_from; +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; +@Entity +@Data +@Table(name = "form_master") +public class FormDefinition { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "form_id") + private String formId; + + @Column(name = "form_name") + private String formName; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "module_id") + private FormModule module; + + @Column(name = "created_at") + private LocalDateTime createdAt = LocalDateTime.now(); + @Column(name = "version") + private Integer version; + +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/data/dynamic_from/FormField.java b/src/main/java/com/iemr/common/data/dynamic_from/FormField.java new file mode 100644 index 00000000..39785ae9 --- /dev/null +++ b/src/main/java/com/iemr/common/data/dynamic_from/FormField.java @@ -0,0 +1,59 @@ +package com.iemr.common.data.dynamic_from; +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Entity +@Data +@Table(name = "form_fields") +public class FormField { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "form_id", referencedColumnName = "form_id") + private FormDefinition form; + + @Column(name = "section_title") + private String sectionTitle; + + @Column(name = "field_id") + private String fieldId; + + @Column(name = "label") + private String label; + + @Column(name = "type") + private String type; + + @Column(name = "is_required") + private Boolean isRequired; + + @Column(name = "is_visible") + private Boolean isVisible; + + @Column(name = "default_value") + private String defaultValue; + + @Column(name = "placeholder") + private String placeholder; + + @Column(name = "options", columnDefinition = "json") + private String options; + + @Column(name = "validation", columnDefinition = "json") + private String validation; // includes error messages now + + @Column(name = "conditional", columnDefinition = "json") + private String conditional; + + @Column(name = "sequence") + private Integer sequence; + + @Column(name = "created_at") + private LocalDateTime createdAt = LocalDateTime.now(); + +} diff --git a/src/main/java/com/iemr/common/data/dynamic_from/FormModule.java b/src/main/java/com/iemr/common/data/dynamic_from/FormModule.java new file mode 100644 index 00000000..ba5d0170 --- /dev/null +++ b/src/main/java/com/iemr/common/data/dynamic_from/FormModule.java @@ -0,0 +1,23 @@ +package com.iemr.common.data.dynamic_from; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Entity +@Data +@Table(name = "form_module") +public class FormModule { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "module_name") + private String moduleName; + + @Column(name = "created_at") + private LocalDateTime createdAt = LocalDateTime.now(); + +} diff --git a/src/main/java/com/iemr/common/data/kmfilemanager/KMFileManager.java b/src/main/java/com/iemr/common/data/kmfilemanager/KMFileManager.java index 6630f367..ed7f0b84 100644 --- a/src/main/java/com/iemr/common/data/kmfilemanager/KMFileManager.java +++ b/src/main/java/com/iemr/common/data/kmfilemanager/KMFileManager.java @@ -110,7 +110,8 @@ public class KMFileManager { @Transient @Expose private Integer categoryID; - @Transient + + @Column(name = "SubCategoryID") @Expose private Integer subCategoryID; @Transient diff --git a/src/main/java/com/iemr/common/dto/dynamicForm/FieldDTO.java b/src/main/java/com/iemr/common/dto/dynamicForm/FieldDTO.java new file mode 100644 index 00000000..894f03c2 --- /dev/null +++ b/src/main/java/com/iemr/common/dto/dynamicForm/FieldDTO.java @@ -0,0 +1,25 @@ +package com.iemr.common.dto.dynamicForm; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class FieldDTO { + private Long id; + private String formId; + private String sectionTitle; + private String fieldId; + private String label; + private String type; + private Boolean isVisible; + private Boolean isRequired; + private String defaultValue; + private String placeholder; + private Integer sequence; + private String options; // ⬅️ changed from String to List + private String validation; // ⬅️ changed from String to Map + private String conditional; // ⬅️ changed from String to Map +} + diff --git a/src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java b/src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java new file mode 100644 index 00000000..3415d91a --- /dev/null +++ b/src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java @@ -0,0 +1,24 @@ +package com.iemr.common.dto.dynamicForm; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class FieldResponseDTO { + private Long id; + private String formId; + private String sectionTitle; + private String fieldId; + private String label; + private Boolean visible; + private String type; + private Boolean isRequired; + private String defaultValue; + private String placeholder; + private Integer sequence; + private List options; + private Map validation; + private Map conditional; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/dto/dynamicForm/FormDTO.java b/src/main/java/com/iemr/common/dto/dynamicForm/FormDTO.java new file mode 100644 index 00000000..24788cac --- /dev/null +++ b/src/main/java/com/iemr/common/dto/dynamicForm/FormDTO.java @@ -0,0 +1,10 @@ +package com.iemr.common.dto.dynamicForm; + +import lombok.Data; + +@Data +public class FormDTO { + private String formId; + private String formName; + private Long moduleId; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/dto/dynamicForm/FormResponseDTO.java b/src/main/java/com/iemr/common/dto/dynamicForm/FormResponseDTO.java new file mode 100644 index 00000000..26563927 --- /dev/null +++ b/src/main/java/com/iemr/common/dto/dynamicForm/FormResponseDTO.java @@ -0,0 +1,13 @@ +package com.iemr.common.dto.dynamicForm; + +import lombok.Data; + +import java.util.List; + +@Data +public class FormResponseDTO { + private Integer version; + private String formId; + private String formName; + private List sections; +} diff --git a/src/main/java/com/iemr/common/dto/dynamicForm/GroupedFieldResponseDTO.java b/src/main/java/com/iemr/common/dto/dynamicForm/GroupedFieldResponseDTO.java new file mode 100644 index 00000000..840c5097 --- /dev/null +++ b/src/main/java/com/iemr/common/dto/dynamicForm/GroupedFieldResponseDTO.java @@ -0,0 +1,11 @@ +package com.iemr.common.dto.dynamicForm; + +import lombok.Data; + +import java.util.List; + +@Data +public class GroupedFieldResponseDTO { + private String sectionTitle; + private List fields; +} diff --git a/src/main/java/com/iemr/common/dto/dynamicForm/ModuleDTO.java b/src/main/java/com/iemr/common/dto/dynamicForm/ModuleDTO.java new file mode 100644 index 00000000..3f4240c4 --- /dev/null +++ b/src/main/java/com/iemr/common/dto/dynamicForm/ModuleDTO.java @@ -0,0 +1,8 @@ +package com.iemr.common.dto.dynamicForm; + +import lombok.Data; + +@Data +public class ModuleDTO { + private String moduleName; +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/dto/grivance/GrievanceWorklistDTO.java b/src/main/java/com/iemr/common/dto/grivance/GrievanceWorklistDTO.java index b8184162..6364b7f6 100644 --- a/src/main/java/com/iemr/common/dto/grivance/GrievanceWorklistDTO.java +++ b/src/main/java/com/iemr/common/dto/grivance/GrievanceWorklistDTO.java @@ -39,13 +39,14 @@ public class GrievanceWorklistDTO implements Serializable { private String age; private Boolean retryNeeded; private Integer callCounter; - private Timestamp lastCall; + private Timestamp lastCall; + private Boolean beneficiaryConsent; public GrievanceWorklistDTO(String complaintID,Long grievanceId, String subjectOfComplaint, String complaint, Long beneficiaryRegID, Integer providerServiceMapID,String primaryNumber,String severety,String state, Integer userId, Boolean deleted, String createdBy, Timestamp createdDate, Timestamp lastModDate, Boolean isCompleted,String firstName, String lastName, String gender, String district, Long beneficiaryID, String age, - Boolean retryNeeded, Integer callCounter, Timestamp lastCall) { + Boolean retryNeeded, Integer callCounter, Timestamp lastCall, Boolean beneficiaryConsent) { super(); this.complaintID = complaintID; this.grievanceId = grievanceId; @@ -71,6 +72,7 @@ public GrievanceWorklistDTO(String complaintID,Long grievanceId, String subjectO this.retryNeeded = retryNeeded; this.callCounter = callCounter; this.lastCall = lastCall; + this.beneficiaryConsent = beneficiaryConsent; } diff --git a/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java b/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java new file mode 100644 index 00000000..4aea5698 --- /dev/null +++ b/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java @@ -0,0 +1,12 @@ +package com.iemr.common.repository.dynamic_form; + +import com.iemr.common.data.dynamic_from.FormField; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface FieldRepository extends JpaRepository { + List findByForm_FormIdOrderBySequenceAsc(String formId); +} diff --git a/src/main/java/com/iemr/common/repository/dynamic_form/FormRepository.java b/src/main/java/com/iemr/common/repository/dynamic_form/FormRepository.java new file mode 100644 index 00000000..50331dd2 --- /dev/null +++ b/src/main/java/com/iemr/common/repository/dynamic_form/FormRepository.java @@ -0,0 +1,14 @@ +package com.iemr.common.repository.dynamic_form; + +import com.iemr.common.data.dynamic_from.FormDefinition; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface FormRepository extends JpaRepository { + Optional findByFormId(String formId); + List findByModule_Id(Long moduleId); +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/repository/dynamic_form/ModuleRepository.java b/src/main/java/com/iemr/common/repository/dynamic_form/ModuleRepository.java index e4eb221e..b0e65872 100644 --- a/src/main/java/com/iemr/common/repository/dynamic_form/ModuleRepository.java +++ b/src/main/java/com/iemr/common/repository/dynamic_form/ModuleRepository.java @@ -1,10 +1,9 @@ package com.iemr.common.repository.dynamic_form; +import com.iemr.common.data.dynamic_from.FormModule; import com.iemr.common.data.dynamic_from.ModuleEntity; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository -public interface ModuleRepository extends CrudRepository { - -} +public interface ModuleRepository extends CrudRepository {} diff --git a/src/main/java/com/iemr/common/repository/kmfilemanager/KMFileManagerRepository.java b/src/main/java/com/iemr/common/repository/kmfilemanager/KMFileManagerRepository.java index aad6a98f..f1e8187d 100644 --- a/src/main/java/com/iemr/common/repository/kmfilemanager/KMFileManagerRepository.java +++ b/src/main/java/com/iemr/common/repository/kmfilemanager/KMFileManagerRepository.java @@ -74,5 +74,9 @@ ArrayList getKMFileLists(@Param("providerServiceMapID") Integer p @Query("select kmFileManager.fileName, kmFileManager.fileExtension from KMFileManager kmFileManager " + "where kmFileManager.fileUID = :fileUID") List getFileNameByUID(@Param("fileUID") String fileUID); + + + @Query("SELECT km FROM KMFileManager km WHERE km.subCategoryID = :subCategoryID AND km.deleted = false") + List getFilesBySubCategoryID(@Param("subCategoryID") Integer subCategoryID); } diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterService.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterService.java new file mode 100644 index 00000000..c603cd65 --- /dev/null +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterService.java @@ -0,0 +1,22 @@ +package com.iemr.common.service.dynamicForm; + +import com.iemr.common.data.dynamic_from.FormDefinition; +import com.iemr.common.data.dynamic_from.FormField; +import com.iemr.common.data.dynamic_from.FormModule; +import com.iemr.common.dto.dynamicForm.FieldDTO; +import com.iemr.common.dto.dynamicForm.FormDTO; +import com.iemr.common.dto.dynamicForm.FormResponseDTO; +import com.iemr.common.dto.dynamicForm.ModuleDTO; + +import java.util.List; + +public interface FormMasterService { + FormModule createModule(ModuleDTO dto); + FormDefinition createForm(FormDTO dto); + List createField(List dto); + FormField updateField(FieldDTO dto); + + FormResponseDTO getStructuredFormByFormId(String formId); + + void deleteField(Long fieldId); +} diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java new file mode 100644 index 00000000..893c1c8a --- /dev/null +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -0,0 +1,177 @@ +package com.iemr.common.service.dynamicForm; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iemr.common.data.dynamic_from.FormDefinition; +import com.iemr.common.data.dynamic_from.FormField; +import com.iemr.common.data.dynamic_from.FormModule; +import com.iemr.common.dto.dynamicForm.*; +import com.iemr.common.repository.dynamic_form.FieldRepository; +import com.iemr.common.repository.dynamic_form.FormRepository; +import com.iemr.common.repository.dynamic_form.ModuleRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.fasterxml.jackson.core.type.TypeReference; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class FormMasterServiceImpl implements FormMasterService { + + @Autowired + private ModuleRepository moduleRepo; + @Autowired private FormRepository formRepo; + @Autowired private FieldRepository fieldRepo; + + @Override + public FormModule createModule(ModuleDTO dto) { + FormModule module = new FormModule(); + module.setModuleName(dto.getModuleName()); + return moduleRepo.save(module); + } + + @Override + public FormDefinition createForm(FormDTO dto) { + FormModule module = moduleRepo.findById(dto.getModuleId()) + .orElseThrow(() -> new IllegalArgumentException("Invalid module ID")); + + FormDefinition form = new FormDefinition(); + form.setFormId(dto.getFormId()); + form.setFormName(dto.getFormName()); + form.setModule(module); + return formRepo.save(form); + } + + @Override + public List createField(List dtoList) { + List savedFields = new ArrayList<>(); + + for (FieldDTO dto : dtoList) { + FormDefinition form = formRepo.findByFormId(dto.getFormId()) + .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); + + FormField field = new FormField(); + field.setForm(form); + field.setSectionTitle(dto.getSectionTitle()); + field.setFieldId(dto.getFieldId()); + field.setLabel(dto.getLabel()); + field.setType(dto.getType()); + field.setIsVisible(dto.getIsVisible()); + field.setIsRequired(dto.getIsRequired()); + field.setDefaultValue(dto.getDefaultValue()); + field.setPlaceholder(dto.getPlaceholder()); + field.setOptions(dto.getOptions()); + field.setValidation(dto.getValidation()); + field.setConditional(dto.getConditional()); + field.setSequence(dto.getSequence()); + + savedFields.add(fieldRepo.save(field)); + } + + return savedFields; + } + + @Override + public FormField updateField(FieldDTO dto) { + FormField field = fieldRepo.findById(dto.getId()) + .orElseThrow(() -> new IllegalArgumentException("Field not found: " + dto.getId())); + field.setId(dto.getId()); + field.setSectionTitle(dto.getSectionTitle()); + field.setLabel(dto.getLabel()); + field.setType(dto.getType()); + field.setIsVisible(dto.getIsVisible()); + field.setIsRequired(dto.getIsRequired()); + field.setDefaultValue(dto.getDefaultValue()); + field.setPlaceholder(dto.getPlaceholder()); + field.setSequence(dto.getSequence()); + field.setOptions(dto.getOptions()); + field.setValidation(dto.getValidation()); + field.setConditional(dto.getConditional()); + + + return fieldRepo.save(field); + } + + @Override + public FormResponseDTO getStructuredFormByFormId(String formId) { + FormDefinition form = formRepo.findByFormId(formId) + .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); + + List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); + ObjectMapper objectMapper = new ObjectMapper(); + + List fieldDtos = fields.stream() + .map(field -> { + FieldResponseDTO dto = new FieldResponseDTO(); + dto.setId(field.getId()); + dto.setVisible(field.getIsVisible()); + dto.setFormId(field.getForm().getFormId()); + dto.setSectionTitle(field.getSectionTitle()); + dto.setFieldId(field.getFieldId()); + dto.setLabel(field.getLabel()); + dto.setType(field.getType()); + dto.setIsRequired(field.getIsRequired()); + dto.setDefaultValue(field.getDefaultValue()); + dto.setPlaceholder(field.getPlaceholder()); + dto.setSequence(field.getSequence()); + + try { + // Handle options + if (field.getOptions() != null && !field.getOptions().isBlank()) { + List options = objectMapper.readValue(field.getOptions(), new TypeReference<>() {}); + dto.setOptions(options.isEmpty() ? null : options); + } else { + dto.setOptions(null); + } + + // Handle validation + if (field.getValidation() != null && !field.getValidation().isBlank()) { + Map validation = objectMapper.readValue(field.getValidation(), new TypeReference<>() {}); + dto.setValidation(validation.isEmpty() ? null : validation); + } else { + dto.setValidation(null); + } + + // Handle conditional + if (field.getConditional() != null && !field.getConditional().isBlank()) { + Map conditional = objectMapper.readValue(field.getConditional(), new TypeReference<>() {}); + dto.setConditional(conditional.isEmpty() ? null : conditional); + } else { + dto.setConditional(null); + } + } catch (JsonProcessingException e) { + System.err.println("JSON Parsing Error in field: " + field.getFieldId()); + e.printStackTrace(); + throw new RuntimeException("Failed to parse JSON for field: " + field.getFieldId(), e); + } + + return dto; + }) + .sorted(Comparator.comparing(FieldResponseDTO::getId)) + .collect(Collectors.toList()); + + + GroupedFieldResponseDTO singleSection = new GroupedFieldResponseDTO(); + singleSection.setSectionTitle("HBNC Form Fields"); // your custom section title + singleSection.setFields(fieldDtos); + + FormResponseDTO response = new FormResponseDTO(); + response.setVersion(form.getVersion()); + response.setFormId(form.getFormId()); + response.setFormName(form.getFormName()); + response.setSections(List.of(singleSection)); + + return response; + } + + + @Override + public void deleteField(Long fieldId) { + fieldRepo.deleteById(fieldId); + } + +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java b/src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java index 5a05b94f..35c9b57b 100644 --- a/src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java +++ b/src/main/java/com/iemr/common/service/grievance/GrievanceDataSyncImpl.java @@ -579,41 +579,48 @@ public String completeGrievanceCall(String request) throws Exception { // Logic for reattempt based on call group type and call type boolean isRetryNeeded = grievanceCallStatus.getRetryNeeded(); - if ((null != grievanceCallStatus.getComplaintResolution() - && grievanceCallStatus.getComplaintResolution().equalsIgnoreCase("Resolved")) || (callGroupType.equalsIgnoreCase("Valid") && (callType.equalsIgnoreCase("Valid") || callType.equals("Test Call")))) { + boolean isResolved = grievanceCallStatus.getComplaintResolution() != null + && grievanceCallStatus.getComplaintResolution().equalsIgnoreCase("Resolved"); + boolean isValidGroup = callGroupType.equalsIgnoreCase("Valid") + && (callType.equalsIgnoreCase("Valid") || callType.equals("Test Call")); + boolean isInvalidGroup = callGroupType.equalsIgnoreCase("Invalid") + && (callType.equalsIgnoreCase("Wrong Number") || callType.equalsIgnoreCase("Invalid Call")); + + if (isResolved) { + // 1) Any resolved call → complete, no retry isRetryNeeded = false; updateCount = grievanceDataRepo.updateCompletedStatusInCall(true, false, complaintID, userID, beneficiaryRegID); - } - else if (callGroupType.equalsIgnoreCase("Invalid") && (callType.equalsIgnoreCase("Wrong Number") || callType.equalsIgnoreCase("Invalid Call"))) { + + } else if (isValidGroup) { + // 2) Valid but not resolved → leave open, retry allowed + isRetryNeeded = true; + updateCount = grievanceDataRepo.updateCompletedStatusInCall(false, true, complaintID, userID, beneficiaryRegID); + + } else if (isInvalidGroup) { + // 3) Invalid calls → complete, no retry isRetryNeeded = false; - updateCount = grievanceDataRepo.updateCompletedStatusInCall(true, isRetryNeeded, complaintID, userID, - beneficiaryRegID); - }else { + updateCount = grievanceDataRepo.updateCompletedStatusInCall(true, false, complaintID, userID, beneficiaryRegID); + + } else { + // 4) All other cases (e.g. unreachable) → leave open, retry allowed isRetryNeeded = true; - updateCount = grievanceDataRepo.updateCompletedStatusInCall(false, isRetryNeeded, complaintID, - userID, beneficiaryRegID); + updateCount = grievanceDataRepo.updateCompletedStatusInCall(false, true, complaintID, userID, beneficiaryRegID); } - // Check if max attempts (3) are reached + + //Call counter update if (isRetryNeeded && grievanceCallStatus.getCallCounter() < grievanceAllocationRetryConfiguration) { grievanceCallStatus.setCallCounter(grievanceCallStatus.getCallCounter() + 1); - updateCallCounter = grievanceDataRepo.updateCallCounter(grievanceCallStatus.getCallCounter(), - isRetryNeeded, grievanceCallRequest.getComplaintID(), - grievanceCallRequest.getBeneficiaryRegID(), - grievanceCallRequest.getUserID()); - if (updateCallCounter > 0) - response = "Successfully closing call"; - else { - response = "failure in closing call"; - } - } else if (grievanceCallStatus.getCallCounter() == grievanceAllocationRetryConfiguration) { - // Max attempts reached, no further reattempt + updateCallCounter = grievanceDataRepo.updateCallCounter( + grievanceCallStatus.getCallCounter(), true, complaintID, beneficiaryRegID, userID); + response = (updateCallCounter > 0) ? "Successfully closing call" : "failure in closing call"; + + } else if (grievanceCallStatus.getCallCounter() >= grievanceAllocationRetryConfiguration) { + // Max attempts reached → treated as “complete” isRetryNeeded = false; - // isCompleted = true; - updateCount = grievanceDataRepo.updateCompletedStatusInCall(isCompleted, isRetryNeeded, complaintID, - userID, beneficiaryRegID); - response = "max_attempts_reached"; // Indicate that max attempts are reached + updateCount = grievanceDataRepo.updateCompletedStatusInCall(true, false, complaintID, userID, beneficiaryRegID); + response = "max_attempts_reached"; - }else if(updateCount > 0) { + } else if (updateCount > 0) { response = "Successfully closing call"; } diff --git a/src/main/java/com/iemr/common/service/grievance/GrievanceHandlingServiceImpl.java b/src/main/java/com/iemr/common/service/grievance/GrievanceHandlingServiceImpl.java index 68e8e76e..ed643417 100644 --- a/src/main/java/com/iemr/common/service/grievance/GrievanceHandlingServiceImpl.java +++ b/src/main/java/com/iemr/common/service/grievance/GrievanceHandlingServiceImpl.java @@ -296,7 +296,7 @@ public List getFormattedGrievanceData(String request) thro // Loop through the worklist data and format the response for (Object[] row : worklistData) { - if (row == null || row.length < 22) + if (row == null || row.length < 24) { logger.warn("invalid row data received"); continue; @@ -334,7 +334,8 @@ public List getFormattedGrievanceData(String request) thro ageFormatted, (Boolean) row[21], // retryNeeded (Integer) row[22], // callCounter - (Timestamp) row[13] //lastCall + (Timestamp) row[13], // lastCall + (Boolean) row[23] //beneficiaryConsent ); diff --git a/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java b/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java index 1a86fad0..7a24c6da 100644 --- a/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java +++ b/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java @@ -125,6 +125,7 @@ public String addKMFile(String request) throws IOException, NoSuchAlgorithmExcep return kmFileManagers.toString(); } + private ArrayList addKMFile(Iterable kmFileManagers) throws IOException, NoSuchAlgorithmException { ArrayList savedFileManagers = new ArrayList(); @@ -175,6 +176,9 @@ private ArrayList addKMFile(Iterable kmFileManager if (uuid != null) { kmFileManager.setKmUploadStatus(KM_UPLOADSTATUS_COMPLETED); kmFileManager.setFileUID(uuid); + + kmFileManager.setSubCategoryID(kmFileManager.getSubCategoryID()); + savedFileManagers.add(kmFileManagerRepository.save(kmFileManager)); if (kmFileManager.getSubCategoryID() != null) { updateSubcategoryFilePath(kmFileManager); @@ -197,6 +201,7 @@ private ArrayList addKMFile(Iterable kmFileManager return savedFileManagers; } + private void updateSubcategoryFilePath(KMFileManager kmFileManager) { subCategoryRepository.updateFilePath(kmFileManager.getSubCategoryID(), kmFileManager.getFileUID()); } diff --git a/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java b/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java index fe081acb..ff6f83e9 100644 --- a/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java +++ b/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java @@ -57,6 +57,7 @@ import com.iemr.common.utils.exception.IEMRException; import com.iemr.common.utils.mapper.InputMapper; import com.iemr.common.data.common.DocFileManager; +import com.iemr.common.data.kmfilemanager.KMFileManager; @Service @PropertySource("classpath:/application.properties") @@ -133,29 +134,45 @@ public Iterable getCategories() { return categoriesList; } + + //newChange @Override public Iterable getSubCategories(String request) throws IEMRException, JsonMappingException, JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - SubCategoryDetails subCategoryDetails = objectMapper.readValue(request, SubCategoryDetails.class); - List subCategoriesList = new ArrayList(); - ArrayList lists = subCategoryRepository.findByCategoryID(subCategoryDetails.getCategoryID()); - for (Object[] objects : lists) { - if (objects != null && objects.length > 1) { - String SubCatFilePath = (String) objects[2]; - String fileUIDAsURI = null; - String fileNameWithExtension = null; - if(SubCatFilePath!=null) { - fileUIDAsURI=getFilePath(SubCatFilePath); - List fileNameList = kmFileManagerRepository.getFileNameByUID(SubCatFilePath); - Object[] fileobjects = fileNameList.get(0); - fileNameWithExtension= (String)fileobjects[0]+ (String) fileobjects[1]; - } - subCategoriesList.add(new SubCategoryDetails((Integer) objects[0], (String) objects[1], SubCatFilePath, fileUIDAsURI, fileNameWithExtension)); - } - } - return subCategoriesList; + ObjectMapper objectMapper = new ObjectMapper(); + SubCategoryDetails subCategoryDetails = objectMapper.readValue(request, SubCategoryDetails.class); + List subCategoriesList = new ArrayList<>(); + ArrayList lists = subCategoryRepository.findByCategoryID(subCategoryDetails.getCategoryID()); + + for (Object[] objects : lists) { + if (objects != null && objects.length > 1) { + Integer subCatId = (Integer) objects[0]; + String subCatName = (String) objects[1]; + + // Fetch all files under this subcategory from KMFileManager + List files = kmFileManagerRepository.getFilesBySubCategoryID(subCatId); + ArrayList fileList = new ArrayList<>(files); + + String fileURL = null; + String fileNameWithExtension = null; + + if (!fileList.isEmpty()) { + KMFileManager firstFile = fileList.get(0); // Just for representative file URL and name + fileURL = getFilePath(firstFile.getFileUID()); + fileNameWithExtension = firstFile.getFileName() + firstFile.getFileExtension(); + } + + SubCategoryDetails subCategory = new SubCategoryDetails(subCatId, subCatName); + subCategory.setFileManger(fileList); // Attach all files here + subCategory.setFileURL(fileURL); // Representative file URL + subCategory.setFileNameWithExtension(fileNameWithExtension); // Representative file name+ext + + subCategoriesList.add(subCategory); + } + } + return subCategoriesList; } + private String getFilePath(String fileUID) { String fileUIDAsURI = null; diff --git a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java index ad9345a1..022fd1f8 100644 --- a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java +++ b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java @@ -194,15 +194,15 @@ public String getSMSTemplates(SMSRequest smsRequest) throws Exception { @Override public String updateSMSTemplate(UpdateSMSRequest smsRequest) throws Exception { - SMSTemplate smsTemplate = null; - SMSTemplate request = smsMapper.updateRequestToSMSTemplate(smsRequest); - int updateCount = smsTemplateRepository.updateSMSTemplate(request.getSmsTemplateID(), request.getDeleted()); - if (updateCount > 0) { - smsTemplate = smsTemplateRepository.findBySmsTemplateID(request.getSmsTemplateID()); - } else { - throw new Exception("Failed to update the result"); - } - return OutputMapper.gsonWithoutExposeRestriction().toJson(smsMapper.smsTemplateToResponse(smsTemplate)); + SMSTemplate smsTemplate = null; + SMSTemplate request = smsMapper.updateRequestToSMSTemplate(smsRequest); + int updateCount = smsTemplateRepository.updateSMSTemplate(request.getSmsTemplateID(), request.getDeleted()); + if (updateCount > 0) { + smsTemplate = smsTemplateRepository.findBySmsTemplateID(request.getSmsTemplateID()); + } else { + throw new Exception("Failed to update the result"); + } + return OutputMapper.gsonWithoutExposeRestriction().toJson(smsMapper.smsTemplateToResponse(smsTemplate)); } @Override @@ -347,9 +347,8 @@ public SMSNotification prepareVideoCallSMS(SMSRequest request, VideoCallParamete sms.setPhoneNo(request.getFacilityPhoneNo()); } } - sms.setSms(smsToSend); - return smsNotification.save(sms); + return smsNotification.save(sms); } @@ -357,8 +356,8 @@ public String getVideoCallData(String methodName, VideoCallParameters videoCall) String variableValue = ""; switch (methodName.toLowerCase()) { case "videoconsultationlink": - variableValue = videoCall.getMeetingLink() != null ? videoCall.getMeetingLink() : ""; - break; + variableValue = videoCall.getMeetingLink() != null ? videoCall.getMeetingLink() : ""; + break; case "consultationdate": if (videoCall.getDateOfCall() != null) { SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -377,7 +376,6 @@ public String getVideoCallData(String methodName, VideoCallParameters videoCall) variableValue = result != null ? result.toString() : ""; break; } - return variableValue.trim(); } diff --git a/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java b/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java index a0efd373..05854843 100644 --- a/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java +++ b/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java @@ -220,16 +220,19 @@ public void setValidator(Validator validator) { this.validator = validator; } + private void checkUserAccountStatus(User user) throws IEMRException { + if (user.getDeleted()) { + throw new IEMRException("Your account is locked or de-activated. Please contact administrator"); + } else if (user.getStatusID() > 2) { + throw new IEMRException("Your account is not active. Please contact administrator"); + } + } + @Override public List userAuthenticate(String userName, String password) throws Exception { List users = iEMRUserRepositoryCustom.findByUserNameNew(userName); if (users.size() != 1) { - throw new IEMRException("User login failed due to incorrect username/password"); - } else { - if (users.get(0).getDeleted()) - throw new IEMRException("Your account is locked or de-activated. Please contact administrator"); - else if (users.get(0).getStatusID() > 2) - throw new IEMRException("Your account is not active. Please contact administrator"); + throw new IEMRException("Invalid username or password"); } int failedAttempt = 0; if (failedLoginAttempt != null) @@ -241,6 +244,7 @@ else if (users.get(0).getStatusID() > 2) int validatePassword; validatePassword = securePassword.validatePassword(password, user.getPassword()); if (validatePassword == 1) { + checkUserAccountStatus(user); int iterations = 1001; char[] chars = password.toCharArray(); byte[] salt = getSalt(); @@ -254,27 +258,37 @@ else if (users.get(0).getStatusID() > 2) iEMRUserRepositoryCustom.save(user); } else if (validatePassword == 2) { + checkUserAccountStatus(user); iEMRUserRepositoryCustom.save(user); } else if (validatePassword == 3) { + checkUserAccountStatus(user); iEMRUserRepositoryCustom.save(user); } else if (validatePassword == 0) { - if (user.getFailedAttempt() + 1 >= failedAttempt) { + if (user.getFailedAttempt() + 1 < failedAttempt) { + user.setFailedAttempt(user.getFailedAttempt() + 1); + user = iEMRUserRepositoryCustom.save(user); + logger.warn("User Password Wrong"); + throw new IEMRException("Invalid username or password"); + } else if (user.getFailedAttempt() + 1 >= failedAttempt) { user.setFailedAttempt(user.getFailedAttempt() + 1); user.setDeleted(true); user = iEMRUserRepositoryCustom.save(user); + logger.warn("User Account has been locked after reaching the limit of {} failed login attempts.", + ConfigProperties.getInteger("failedLoginAttempt")); + throw new IEMRException( - "User login failed due to incorrect username/password. Your account is locked due to " - + ConfigProperties.getInteger("failedLoginAttempt") - + " failed attempts. Please contact administrator."); + "Invalid username or password. Please contact administrator."); } else { user.setFailedAttempt(user.getFailedAttempt() + 1); user = iEMRUserRepositoryCustom.save(user); - throw new IEMRException("User login failed due to incorrect username/password. " - + (ConfigProperties.getInteger("failedLoginAttempt") - user.getFailedAttempt()) - + " more attempt left."); + logger.warn("Failed login attempt {} of {} for a user account.", + user.getFailedAttempt(), ConfigProperties.getInteger("failedLoginAttempt")); + throw new IEMRException( + "Invalid username or password. Please contact administrator."); } } else { + checkUserAccountStatus(user); if (user.getFailedAttempt() != 0) { user.setFailedAttempt(0); user = iEMRUserRepositoryCustom.save(user); @@ -307,12 +321,7 @@ public User superUserAuthenticate(String userName, String password) throws Excep List users = iEMRUserRepositoryCustom.findByUserName(userName); if (users.size() != 1) { - throw new IEMRException("User login failed due to incorrect username/password"); - } else { - if (users.get(0).getDeleted()) - throw new IEMRException("Your account is locked or de-activated. Please contact administrator"); - else if (users.get(0).getStatusID() > 2) - throw new IEMRException("Your account is not active. Please contact administrator"); + throw new IEMRException("Invalid username or password"); } int failedAttempt = 0; if (failedLoginAttempt != null) @@ -324,6 +333,7 @@ else if (users.get(0).getStatusID() > 2) int validatePassword; validatePassword = securePassword.validatePassword(password, user.getPassword()); if (validatePassword == 1) { + checkUserAccountStatus(user); int iterations = 1001; char[] chars = password.toCharArray(); byte[] salt = getSalt(); @@ -337,25 +347,34 @@ else if (users.get(0).getStatusID() > 2) iEMRUserRepositoryCustom.save(user); } else if (validatePassword == 2) { + checkUserAccountStatus(user); iEMRUserRepositoryCustom.save(user); } else if (validatePassword == 0) { - if (user.getFailedAttempt() + 1 >= failedAttempt) { + if (user.getFailedAttempt() + 1 < failedAttempt) { + user.setFailedAttempt(user.getFailedAttempt() + 1); + user = iEMRUserRepositoryCustom.save(user); + logger.warn("User Password Wrong"); + throw new IEMRException("Invalid username or password"); + } else if (user.getFailedAttempt() + 1 >= failedAttempt) { user.setFailedAttempt(user.getFailedAttempt() + 1); user.setDeleted(true); user = iEMRUserRepositoryCustom.save(user); + logger.warn("User Account has been locked after reaching the limit of {} failed login attempts.", + ConfigProperties.getInteger("failedLoginAttempt")); + throw new IEMRException( - "User login failed due to incorrect username/password. Your account is locked due to " - + ConfigProperties.getInteger("failedLoginAttempt") - + " failed attempts. Please contact administrator."); + "Invalid username or password. Please contact administrator."); } else { user.setFailedAttempt(user.getFailedAttempt() + 1); user = iEMRUserRepositoryCustom.save(user); - throw new IEMRException("User login failed due to incorrect username/password. " - + (ConfigProperties.getInteger("failedLoginAttempt") - user.getFailedAttempt()) - + " more attempt left."); + logger.warn("Failed login attempt {} of {} for a user account.", + user.getFailedAttempt(), ConfigProperties.getInteger("failedLoginAttempt")); + throw new IEMRException( + "Invalid username or password. Please contact administrator."); } } else { + checkUserAccountStatus(user); if (user.getFailedAttempt() != 0) { user.setFailedAttempt(0); user = iEMRUserRepositoryCustom.save(user); @@ -376,10 +395,10 @@ public LoginResponseModel userAuthenticateV1(LoginRequestModel loginRequest, Str User user = users.get(0); try { if (!securePassword.validatePasswordExisting(loginRequest.getPassword(), user.getPassword())) { - throw new IEMRException("User login failed due to incorrect username/password"); + throw new IEMRException("Invalid username or password"); } } catch (Exception e) { - throw new IEMRException("User login failed due to incorrect username/password"); + throw new IEMRException("Invalid username or password"); } loginResponseModel = userMapper.userDataToLoginResponse(user); logger.info("Login response is " + loginResponseModel.toString()); @@ -391,7 +410,7 @@ public LoginResponseModel userAuthenticateV1(LoginRequestModel loginRequest, Str // loginResponseModel.setHostName(hostName); // loginResponseModel.setIpAddress(ipAddress); } else { - throw new IEMRException("User login failed due to incorrect username/password"); + throw new IEMRException("Invalid username or password"); } return loginResponseModel; @@ -614,7 +633,8 @@ public String saveUserSecurityQuesAns(Iterable m_UserSecur User users = iEMRUserRepositoryCustom .findUserByUserID(m_UserSecurityQMapping.iterator().next().getUserID()); if (users == null) { - throw new IEMRException("User does not exist or is not active"); + logger.warn("User validation failed: user not found for provided ID."); + throw new IEMRException("Invalid user. Please contact administrator."); } Iterable obj = iEMRUserSecurityQuesAnsRepository.saveAll(m_UserSecurityQMapping); @@ -627,7 +647,9 @@ public String saveUserSecurityQuesAns(Iterable m_UserSecur sessionObject.deleteSessionObject((users.getUserID().toString() + users.getUserName())); return generateTransactionIdForPasswordChange(users); } else { - throw new IEMRException("Failed to save security question and answers, Please try again"); + logger.error( + "Failed to save user security questions. Repository save operation returned empty result."); + throw new IEMRException("Unable to complete the operation. Please try again later."); } } else throw new IEMRException("Invalid user, please contact administrator"); @@ -867,9 +889,9 @@ public JSONObject generateKeyAndValidateIP(JSONObject responseObj, String ipAddr throws JSONException, NoSuchAlgorithmException, IEMRException { String key = generateKey(responseObj); // commented the below code to restrict IP address and hostname to be sent on UI -// responseObj.put("loginIPAddress", ipAddress); -// responseObj.put("ipAddress", ipAddress); -// responseObj.put("hostName", hostName); + // responseObj.put("loginIPAddress", ipAddress); + // responseObj.put("ipAddress", ipAddress); + // responseObj.put("hostName", hostName); responseObj = validator.updateCacheObj(responseObj, key, ""); setConcurrentCheckSessionObject(responseObj, key); return responseObj; @@ -958,10 +980,10 @@ public void userForceLogout(ForceLogoutRequestModel request) throws Exception { } try { if (!securePassword.validatePasswordExisting(request.getPassword(), users.get(0).getPassword())) { - throw new Exception("Force logout failed due to incorrect password"); + throw new Exception("Force logout failed"); } } catch (Exception e) { - throw new Exception("Force logout failed due to incorrect password"); + throw new Exception("Force logout failed"); } userForceLogout(request, users.get(0)); } @@ -1049,15 +1071,15 @@ public List userAuthenticateByEncryption(String req) throws Exception { LoginRequestModel m_user = InputMapper.gson().fromJson(jsonreq, LoginRequestModel.class); List users = iEMRUserRepositoryCustom.findByUserName(m_user.getUserName()); if (users.size() != 1) { - throw new IEMRException("User login failed due to incorrect username/password"); + throw new IEMRException("Invalid username or password"); } User user = users.get(0); try { if (!securePassword.validatePasswordExisting(m_user.getPassword(), user.getPassword())) { - throw new IEMRException("User login failed due to incorrect username/password"); + throw new IEMRException("Invalid username or password"); } } catch (Exception e) { - throw new IEMRException("User login failed due to incorrect username/password"); + throw new IEMRException("Invalid username or password"); } user.setM_UserServiceRoleMapping(getUserServiceRoleMapping(user.getUserID())); return users; @@ -1105,7 +1127,10 @@ public String validateQuestionAndAnswersForPasswordChange(JsonObject request) th if (request.has("userName") && request.get("userName") != null) { List users = iEMRUserRepositoryCustom.findByUserName(request.get("userName").getAsString()); if (users.size() != 1) { - throw new IEMRException("User does not exist or is not active or more than 1 user found"); + logger.warn("User validation failed: not found or duplicate entries for username '{}'", + request.get("userName").getAsString()); + throw new IEMRException("Unable to validate credentials. Please contact administrator."); + } User user = users.get(0); sessionObject.deleteSessionObject((user.getUserID().toString() + user.getUserName())); @@ -1118,8 +1143,12 @@ public String validateQuestionAndAnswersForPasswordChange(JsonObject request) th user.getUserID(), securityAnswers.getQuestionId(), securityAnswers.getAnswer()); if (userSecurityQuestionAnswers == null - || userSecurityQuestionAnswers.getUserSecurityQAID() == null) - throw new IEMRException("Security answers does not match"); + || userSecurityQuestionAnswers.getUserSecurityQAID() == null) { + logger.warn("Security answer mismatch for userId={}, questionId={}", + user.getUserID(), securityAnswers.getQuestionId()); + throw new IEMRException( + "We couldn't verify your answers. Please try again"); + } pointer++; } @@ -1132,7 +1161,7 @@ public String validateQuestionAndAnswersForPasswordChange(JsonObject request) th throw new IEMRException("Invalid questions, validation failed, please contact administrator"); } else - throw new IEMRException("Invalid/NULL user name"); + throw new IEMRException("Invalid request. Please try again."); } catch (Exception e) { logger.error(e.getMessage(), e); throw new IEMRException(e.getMessage()); diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java index 6806834d..9db1a771 100644 --- a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java @@ -33,12 +33,14 @@ public class VideoCallServiceImpl implements VideoCallService { @Autowired private VideoCallMapper videoCallMapper; - @Value("${video-call-url}") private String meetingLink; private boolean isLinkSent = false; private String consultationStatus = "Not Initiated"; + + @Value("${video-call-url}") private String jitsiLink; + public VideoCallServiceImpl() { // this.jitsiLink = ConfigProperties.getPropertyByName("video-call-url"); // logger.info("Jitsi Link fetched: " + this.jitsiLink); @@ -46,7 +48,8 @@ public VideoCallServiceImpl() { @Override public String generateMeetingLink() { - meetingLink=jitsiLink+RandomStringUtils.randomAlphanumeric(8); + logger.info("Jitsi Link: " + jitsiLink); + meetingLink=jitsiLink+"m="+RandomStringUtils.randomAlphanumeric(8); logger.info("Meeting link: " + meetingLink); return meetingLink; } @@ -72,7 +75,10 @@ public String sendMeetingLink(VideoCallRequest request) throws Exception { response.setResponse(responseData.toJson()); return OutputMapper.gsonWithoutExposeRestriction() - .toJson(response); + .toJson(response) .replace("\\u003d", "=") + .replace("\\u003c", "<") + .replace("\\u003e", ">") + .replace("\\u0026", "&"); } @Override diff --git a/src/main/java/com/iemr/common/utils/exception/GlobalExceptionHandler.java b/src/main/java/com/iemr/common/utils/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..90a06c79 --- /dev/null +++ b/src/main/java/com/iemr/common/utils/exception/GlobalExceptionHandler.java @@ -0,0 +1,36 @@ +package com.iemr.common.utils.exception; + +import com.iemr.common.utils.response.ApiResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.stream.Collectors; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException(Exception ex) { + ex.printStackTrace(); // Log for debugging + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ApiResponse.error("Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR.value(),null)); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidationException(MethodArgumentNotValidException ex) { + String errorMessage = ex.getBindingResult().getFieldErrors().stream() + .map(error -> error.getField() + ": " + error.getDefaultMessage()) + .collect(Collectors.joining(", ")); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(ApiResponse.error("Validation Error: " + errorMessage, HttpStatus.BAD_REQUEST.value(),null)); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgException(IllegalArgumentException ex) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(ApiResponse.error(ex.getMessage(), HttpStatus.BAD_REQUEST.value(),null)); + } +} diff --git a/src/main/java/com/iemr/common/utils/response/ApiResponse.java b/src/main/java/com/iemr/common/utils/response/ApiResponse.java new file mode 100644 index 00000000..928a2a25 --- /dev/null +++ b/src/main/java/com/iemr/common/utils/response/ApiResponse.java @@ -0,0 +1,31 @@ +package com.iemr.common.utils.response; + +import lombok.Data; + +@Data +public class ApiResponse { + private boolean success; + private String message; + private Integer statusCode; + + private T data; + + public ApiResponse() {} + + public ApiResponse(boolean success, String message, Integer statusCode,T data) { + this.success = success; + this.message = message; + this.data = data; + this.statusCode = statusCode; + } + + public static ApiResponse success(String message,Integer statusCode, T data) { + return new ApiResponse<>(true, message,statusCode, data); + } + + public static ApiResponse error(String message,Integer statusCode, T data) { + return new ApiResponse<>(false, message, statusCode,data); + } + + // getters and setters +}