Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
341a34f
Enforced mandatory validation for "Reason for Refusal in section e in…
sehjotsinghunthinkable Jun 22, 2026
2908c20
Added counselled flag to confirmed cases response, indicating whether…
sehjotsinghunthinkable Jun 23, 2026
9396905
Align `/complete` and `/submitBulk` form response editing behavior
sehjotsinghunthinkable Jun 23, 2026
9e28141
Merge branch 'vb/counselling' into vb/counselling
sehjotsinghunthinkable Jun 23, 2026
105ede6
Add van/sync tracking fields (vanID, parkingPlaceID, vanSerialNo, pro…
sehjotsinghunthinkable Jun 24, 2026
21b7187
feat(dynamic-form): add Hindi localization fields and completed benef…
sehjotsinghunthinkable Jun 25, 2026
fdb1a1f
feat(dynamic-form): add Hindi localization fields and completed benef…
sehjotsinghunthinkable Jun 25, 2026
6d8cb0b
Merge branch 'vb/counselling' into vb/counselling
vishwab1 Jun 25, 2026
5f05f66
feat(beneficiary): expose GPS coordinates in getBeneficiaryData response
sehjotsinghunthinkable Jun 26, 2026
6ae9ed0
Committed 82b00ccf with only the three files:
sehjotsinghunthinkable Jun 26, 2026
0332e9b
Revert "Committed 82b00ccf with only the three files:"
sehjotsinghunthinkable Jun 26, 2026
4123985
Committed 82b00ccf with only the three files:
sehjotsinghunthinkable Jun 26, 2026
b7488fe
Committed 82b00ccf with only the three files:
sehjotsinghunthinkable Jun 26, 2026
78d402c
Merge branch 'vb/counselling' into vb/counselling
sehjotsinghunthinkable Jun 26, 2026
b92cdea
Updated isMandatory field in Section C, so changed in TbCounsellingFo…
sehjotsinghunthinkable Jun 30, 2026
81e29d2
Add optional villageId and providerServiceMapId filters to getComplet…
sehjotsinghunthinkable Jul 2, 2026
be0d0d6
Removing section f from seeder
sehjotsinghunthinkable Jul 2, 2026
f6aca3a
Removing already existing check in /complete api
sehjotsinghunthinkable Jul 2, 2026
41550c2
Add a CHECKBOX question type and a TB_COUNSELLING_V2 seeder with a GE…
sehjotsinghunthinkable Jul 5, 2026
380014c
Seed single-checkbox questions for Sections A-D and point the Counsel…
sehjotsinghunthinkable Jul 6, 2026
4aafc4b
Mark form/section status REFUSED when TB2_GI_Q1=NO on /complete, spli…
sehjotsinghunthinkable Jul 6, 2026
b4bc4e0
Resolved conflicts
sehjotsinghunthinkable Jul 7, 2026
c53b9b5
fix(stoptb): getBeneficiaryData reads anthropometry/vitals from stand…
vishwab1 Jun 30, 2026
c7806cf
fix(compile): rename variable 'a' to 'anthro' to avoid conflict with …
vishwab1 Jun 30, 2026
d8008c3
fix(stoptb): set CreatedDate on t_benvisitdetail insert to prevent NO…
vishwab1 Jun 30, 2026
095f0ae
fix(anc-pnc): guard CreatedDate on BenVisitDetail insert for ANC and …
vishwab1 Jun 30, 2026
3b3a4c5
fix(docker): set TZ=Asia/Kolkata to prevent UTC/IST date mismatch
vishwab1 Jul 1, 2026
b1d28ae
fix(stoptb): handle ISO 8601 Z suffix in parseDob to fix beneficiary …
vishwab1 Jul 1, 2026
6d92f74
fix(stoptb): fallback to i_ben_flow_outreach.ben_dob when i_beneficia…
vishwab1 Jul 1, 2026
7459c47
fix: use saveAndFlush to prevent FK violation in t_benchiefcomplaint
vishwab1 Jul 1, 2026
9fccf6f
fix(stoptb): compute age from benDetailsRMNCH_OBJ.dob instead of benD…
vishwab1 Jul 3, 2026
7ddde0a
fix(stoptb): scope Suspected/Confirmed getAll by facility+village ins…
vishwab1 Jul 3, 2026
33c4f4d
fix(stoptb): drop date filter from facility-scoped Suspected query
vishwab1 Jul 3, 2026
349e714
fix(stoptb): join facility-scoped query on beneficiaryID, not benefic…
vishwab1 Jul 3, 2026
4d6606e
Added counselled flag to confirmed cases response, indicating whether…
sehjotsinghunthinkable Jun 23, 2026
33219a1
Revert "Committed 82b00ccf with only the three files:"
sehjotsinghunthinkable Jun 26, 2026
6951221
Committed 82b00ccf with only the three files:
sehjotsinghunthinkable Jun 26, 2026
e9fd699
Merge branch 'vb/counselling' into vb/counselling
vishwab1 Jul 7, 2026
e2cb73b
feat(beneficiary): expose GPS coordinates in getBeneficiaryData response
sehjotsinghunthinkable Jun 26, 2026
043ab84
Changing working of checkbox as of radio button
sehjotsinghunthinkable Jul 7, 2026
22202ff
Merge branch 'vb/counselling' into vb/counselling
sehjotsinghunthinkable Jul 7, 2026
9a743b6
fix(counselling): make /submitBulk update sections in place (respecti…
sehjotsinghunthinkable Jul 10, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/
package com.iemr.flw.controller;

import com.iemr.flw.dto.iemr.CompletedBeneficiariesDTO;
import com.iemr.flw.dto.iemr.CompletedBeneficiaryDetailDTO;
import com.iemr.flw.dto.iemr.FormResponseDTO;
import com.iemr.flw.dto.iemr.FormResponseRequest;
import com.iemr.flw.masterEnum.FormType;
Expand Down Expand Up @@ -99,13 +99,13 @@ public ResponseEntity<ApiResponse> getById(@RequestParam Long responseId) {
FormResponseDTO dto = responseService.getResponseById(responseId);
return ResponseEntity.ok(new ApiResponse(true, "Response fetched successfully", dto));
}
@Operation(summary = "Get beneficiary IDs that have a COMPLETE or REFUSED form response for the given form type, optionally filtered by village and/or provider service map")
@Operation(summary = "Get COMPLETE/REFUSED form-response details (beneficiaryId, refusal flag, sections filled vs total) for the given form type, optionally filtered by village and/or provider service map")
@RequestMapping(value = "/getCompletedBeneficiaries", method = RequestMethod.GET)
public ResponseEntity<ApiResponse> getCompletedBeneficiaries(
@RequestParam FormType formType,
@RequestParam(required = false) Integer villageId,
@RequestParam(required = false) Integer providerServiceMapId) {
CompletedBeneficiariesDTO result =
List<CompletedBeneficiaryDetailDTO> result =
responseService.getCompletedBeneficiaries(formType, villageId, providerServiceMapId);
return ResponseEntity.ok(new ApiResponse(true, "Completed beneficiaries fetched successfully", result));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
* Beneficiary IDs for a form type, split by outcome: those who completed the form and those
* who refused (TB2_GI_Q1 = NO on the General Info consent question).
* Per-beneficiary detail for a COMPLETE/REFUSED form response: outcome plus section-fill progress
* against the exact form version that response was submitted against.
*
* @author Piramal Swasthya
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CompletedBeneficiariesDTO {
private List<Long> completed;
private List<Long> refused;
}
public class CompletedBeneficiaryDetailDTO {
private Long beneficiaryId;
private boolean isRefused;
private int sectionsFilled;
private int totalSections;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* A single question's answer within a section answer request.
* Exactly one of optionValue, optionValues, answerText, or answerDate should be set per question type:
* RADIO → optionValue (single string)
* CHECKBOX → optionValue (single string, e.g. "CHECKED")
* MCQ → optionValues (list of strings, one row saved per element)
* TEXT/AUTO_FILL → answerText
* DATE → answerDate (ISO date string) or answerText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class QuestionResponseDTO {

private Long questionResponseId;
private Long questionId;
/** Populated for RADIO and MCQ answers. */
/** Populated for RADIO, MCQ, and CHECKBOX answers. */
private Long optionId;
/** Populated for TEXT, DATE, AUTO_FILL answers. */
private String answerText;
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/iemr/flw/masterEnum/QuestionType.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public enum QuestionType {
RADIO,
/** Multi-select from a predefined list of options. */
MCQ,
/** Single boolean checkbox — checked or unchecked. May carry options for display
* purposes; answers are still stored as free text. */
/** Single boolean checkbox — checked or unchecked. Resolved like RADIO: submit
* optionValue to select it, omit to leave unanswered. */
CHECKBOX,
/** Free-text input. */
TEXT,
Expand Down
14 changes: 7 additions & 7 deletions src/main/java/com/iemr/flw/repo/iemr/FormResponseRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,17 @@ public interface FormResponseRepo extends JpaRepository<FormResponse, Long> {

List<FormResponse> findByBeneficiaryIdAndFormId(Long beneficiaryId, Long formId);

@Query("SELECT r.beneficiaryId FROM FormResponse r WHERE r.formId = :formId AND r.status = :status")
List<Long> findBeneficiaryIdsByFormIdAndStatus(@Param("formId") Long formId, @Param("status") String status);
@Query("SELECT r FROM FormResponse r WHERE r.formId = :formId AND r.status IN :statuses")
List<FormResponse> findByFormIdAndStatusIn(@Param("formId") Long formId, @Param("statuses") List<String> statuses);

@Query("SELECT r.beneficiaryId FROM FormResponse r WHERE r.formId = :formId AND r.status = :status " +
@Query("SELECT r FROM FormResponse r WHERE r.formId = :formId AND r.status IN :statuses " +
"AND r.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<Long> findBeneficiaryIdsByFormIdAndStatusFiltered(@Param("formId") Long formId,
@Param("status") String status,
@Param("villageId") Integer villageId,
@Param("providerServiceMapId") Integer providerServiceMapId);
List<FormResponse> findByFormIdAndStatusInFiltered(@Param("formId") Long formId,
@Param("statuses") List<String> statuses,
@Param("villageId") Integer villageId,
@Param("providerServiceMapId") Integer providerServiceMapId);

@Query("SELECT r.beneficiaryId FROM FormResponse r " +
"WHERE r.beneficiaryId IN :benIds " +
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/iemr/flw/repo/iemr/FormSectionRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@
package com.iemr.flw.repo.iemr;

import com.iemr.flw.domain.iemr.FormSection;
import com.iemr.flw.masterEnum.SectionPhase;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.List;

/**
Expand All @@ -35,4 +39,11 @@
public interface FormSectionRepo extends JpaRepository<FormSection, Long> {

List<FormSection> findByFormVersion_VersionIdOrderByDisplayOrderAsc(Long versionId);

@Query("SELECT fs.formVersion.versionId, COUNT(fs) FROM FormSection fs " +
"WHERE fs.formVersion.versionId IN :versionIds " +
"AND fs.sectionPhase = :sectionPhase " +
"GROUP BY fs.formVersion.versionId")
List<Object[]> countByFormVersion_VersionIdInAndSectionPhase(@Param("versionIds") Collection<Long> versionIds,
@Param("sectionPhase") SectionPhase sectionPhase);
}
10 changes: 10 additions & 0 deletions src/main/java/com/iemr/flw/repo/iemr/SectionResponseRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
package com.iemr.flw.repo.iemr;

import com.iemr.flw.domain.iemr.SectionResponse;
import com.iemr.flw.masterEnum.SectionPhase;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.Collection;
Expand All @@ -44,4 +47,11 @@ public interface SectionResponseRepo extends JpaRepository<SectionResponse, Long
List<SectionResponse> findByResponseIdIn(Collection<Long> responseIds);

void deleteByResponseId(Long responseId);

@Query("SELECT sr.responseId, COUNT(sr) FROM SectionResponse sr, FormSection fs " +
"WHERE sr.sectionId = fs.sectionId " +
"AND fs.sectionPhase = :sectionPhase " +
"AND sr.responseId IN :responseIds GROUP BY sr.responseId")
List<Object[]> countByResponseIdInAndSectionPhase(@Param("responseIds") Collection<Long> responseIds,
@Param("sectionPhase") SectionPhase sectionPhase);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/
package com.iemr.flw.service;

import com.iemr.flw.dto.iemr.CompletedBeneficiariesDTO;
import com.iemr.flw.dto.iemr.CompletedBeneficiaryDetailDTO;
import com.iemr.flw.dto.iemr.FormResponseDTO;
import com.iemr.flw.dto.iemr.FormResponseRequest;
import com.iemr.flw.masterEnum.FormType;
Expand Down Expand Up @@ -63,8 +63,9 @@ public interface DynamicFormResponseService {
List<FormResponseDTO> findPendingFollowUps(List<Long> formIds, int delayDays);

/**
* Returns beneficiary IDs for the given form type split by outcome — COMPLETE and REFUSED —
* optionally filtered by village and/or provider service map.
* Returns per-beneficiary detail (outcome plus section-fill progress) for COMPLETE and REFUSED
* responses to the given form type, optionally filtered by village and/or provider service map.
* sectionsFilled/totalSections only count PRE_SUBMIT-phase sections.
*/
CompletedBeneficiariesDTO getCompletedBeneficiaries(FormType formType, Integer villageId, Integer providerServiceMapId);
List<CompletedBeneficiaryDetailDTO> getCompletedBeneficiaries(FormType formType, Integer villageId, Integer providerServiceMapId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,25 @@ private String getMappingsForAddressIDs(List<RMNCHMBeneficiaryaddress> addressLi
if (benDetailsRMNCH_OBJ.getGpsLongitude() != null)
benDetailsRMNCH_OBJ.setLongitude(BigDecimal.valueOf(benDetailsRMNCH_OBJ.getGpsLongitude()));

// GPS fallback: if not in RMNCH details (syncdatatoamrti not yet called),
// pull from i_beneficiaryaddress (saved during TM-API registration)
if (benDetailsRMNCH_OBJ.getGpsLatitude() == null && benAddressOBJ.getGpsLatitude() != null)
benDetailsRMNCH_OBJ.setGpsLatitude(benAddressOBJ.getGpsLatitude());
if (benDetailsRMNCH_OBJ.getGpsLongitude() == null && benAddressOBJ.getGpsLongitude() != null)
benDetailsRMNCH_OBJ.setGpsLongitude(benAddressOBJ.getGpsLongitude());
if (benDetailsRMNCH_OBJ.getDigipin() == null && benAddressOBJ.getDigipin() != null)
benDetailsRMNCH_OBJ.setDigipin(benAddressOBJ.getDigipin());
if (benDetailsRMNCH_OBJ.getGpsTimestamp() == null && benAddressOBJ.getGpsTimestamp() != null)
benDetailsRMNCH_OBJ.setGpsTimestamp(benAddressOBJ.getGpsTimestamp());
if (benDetailsRMNCH_OBJ.getIsGpsUnavailable() == null && benAddressOBJ.getIsGpsUnavailable() != null)
benDetailsRMNCH_OBJ.setIsGpsUnavailable(benAddressOBJ.getIsGpsUnavailable());

// Map GPS double fields to the exposed latitude/longitude BigDecimal fields for response
if (benDetailsRMNCH_OBJ.getGpsLatitude() != null)
benDetailsRMNCH_OBJ.setLatitude(BigDecimal.valueOf(benDetailsRMNCH_OBJ.getGpsLatitude()));
if (benDetailsRMNCH_OBJ.getGpsLongitude() != null)
benDetailsRMNCH_OBJ.setLongitude(BigDecimal.valueOf(benDetailsRMNCH_OBJ.getGpsLongitude()));

// -----------------------------------------------------------------------------

// related benids
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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/.
*/
package com.iemr.flw.service.impl;

import com.iemr.flw.domain.iemr.FormSection;
import com.iemr.flw.dto.iemr.FormResponseRequest;
import com.iemr.flw.dto.iemr.QuestionAnswerRequest;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* TB2_SEC_GENERAL_INFO's consent question (TB2_GI_Q1) gates the rest of the TB Counselling
* form; a "NO" answer means the beneficiary refused counselling. Shared by /complete and
* /submitBulk so both apply the same refusal rule.
*
* @author Piramal Swasthya
*/
@Component
public class ConsentRefusalEvaluator {

private static final String SECTION_UUID_GENERAL_INFO = "TB2_SEC_GENERAL_INFO";
private static final String QUESTION_UUID_CONSENT = "TB2_GI_Q1";
private static final String OPTION_VALUE_CONSENT_REFUSED = "NO";

public boolean isRefusalAnswer(QuestionAnswerRequest answer) {
return QUESTION_UUID_CONSENT.equals(answer.getQuestionUuid())
&& OPTION_VALUE_CONSENT_REFUSED.equals(answer.getOptionValue());
}

// Scanned directly off the raw request (before any FormSection is resolved) so the
// top-level FormResponse status can be decided. Absence-tolerant: no TB2_SEC_GENERAL_INFO
// section, no answers, or no TB2_GI_Q1 answer at all just means "not refused".
public boolean isConsentRefused(FormResponseRequest request) {
if (request.getSections() == null) {
return false;
}
return request.getSections().stream()
.filter(s -> SECTION_UUID_GENERAL_INFO.equals(s.getSectionUuid()))
.filter(s -> s.getAnswers() != null)
.flatMap(s -> s.getAnswers().stream())
.anyMatch(this::isRefusalAnswer);
}

// Same TB2_GI_Q1="NO" signal as isConsentRefused, but resolved per-section so callers can
// set a section's own status. Absence-tolerant by design: any other section, a missing
// answers list, or no answer for the consent question at all just falls through to
// doneStatus.
public String determineSectionStatus(FormSection section, List<QuestionAnswerRequest> answers,
String doneStatus, String refusedStatus) {
if (!SECTION_UUID_GENERAL_INFO.equals(section.getSectionUuid()) || answers == null) {
return doneStatus;
}
boolean refused = answers.stream().anyMatch(this::isRefusalAnswer);
return refused ? refusedStatus : doneStatus;
}
}
Loading