Skip to content
Closed
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
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.common.flw</groupId>
<artifactId>flw-api</artifactId>
<version>3.9.1</version>
<version>3.12.0</version>
<packaging>war</packaging>

<name>FLW-API</name>
Expand Down
2 changes: 2 additions & 0 deletions src/main/environment/common_ci.properties
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ diagnostic.emrlite.xray.refresh-url=${diagnostic.emrlite.xray.base-url}/api/v1/i
diagnostic.emrlite.xray.order-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/
diagnostic.emrlite.xray.result-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/result/
diagnostic.emrlite.xray.ping-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/ping/
diagnostic.emrlite.xray.cancel-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/cancel/
diagnostic.emrlite.xray.username=@env.DIAGNOSTIC_EMRLITE_XRAY_USERNAME@
diagnostic.emrlite.xray.password=@env.DIAGNOSTIC_EMRLITE_XRAY_PASSWORD@

Expand All @@ -78,6 +79,7 @@ diagnostic.emrlite.truenat.refresh-url=${diagnostic.emrlite.truenat.base-url}/ap
diagnostic.emrlite.truenat.order-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/
diagnostic.emrlite.truenat.result-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/result/
diagnostic.emrlite.truenat.ping-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/ping/
diagnostic.emrlite.truenat.cancel-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/cancel/
diagnostic.emrlite.truenat.username=@env.DIAGNOSTIC_EMRLITE_TRUENAT_USERNAME@
diagnostic.emrlite.truenat.password=@env.DIAGNOSTIC_EMRLITE_TRUENAT_PASSWORD@

Expand Down
2 changes: 2 additions & 0 deletions src/main/environment/common_docker.properties
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ diagnostic.emrlite.xray.refresh-url=${diagnostic.emrlite.xray.base-url}/api/v1/i
diagnostic.emrlite.xray.order-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/
diagnostic.emrlite.xray.result-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/result/
diagnostic.emrlite.xray.ping-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/ping/
diagnostic.emrlite.xray.cancel-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/cancel/
diagnostic.emrlite.xray.username=${DIAGNOSTIC_EMRLITE_XRAY_USERNAME}
diagnostic.emrlite.xray.password=${DIAGNOSTIC_EMRLITE_XRAY_PASSWORD}

Expand All @@ -77,6 +78,7 @@ diagnostic.emrlite.truenat.refresh-url=${diagnostic.emrlite.truenat.base-url}/ap
diagnostic.emrlite.truenat.order-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/
diagnostic.emrlite.truenat.result-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/result/
diagnostic.emrlite.truenat.ping-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/ping/
diagnostic.emrlite.truenat.cancel-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/cancel/
diagnostic.emrlite.truenat.username=${DIAGNOSTIC_EMRLITE_TRUENAT_USERNAME}
diagnostic.emrlite.truenat.password=${DIAGNOSTIC_EMRLITE_TRUENAT_PASSWORD}

Expand Down
6 changes: 6 additions & 0 deletions src/main/environment/common_example.properties
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ abha.client.secret=<your-abha-client-secret>
abha.token.url=<abha-token-url>
abha.xcmid=<sbx-or-prod>

# GovThealth API Config
govthealth.user.details.url={$GOVTHEALTH_USER_DETAILS_URL}
govthealth.user.id={$GOVTHEALTH_USER_ID}
govthealth.password={$GOVTHEALTH_PASSWORD}

# ── Diagnostic Device Integration ────────────────────────────────────────────
# diagnostic.provider.xray / diagnostic.provider.truenat select the vendor per orderType family
Expand All @@ -64,6 +68,7 @@ diagnostic.emrlite.xray.refresh-url=${diagnostic.emrlite.xray.base-url}/api/v1/i
diagnostic.emrlite.xray.order-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/
diagnostic.emrlite.xray.result-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/result/
diagnostic.emrlite.xray.ping-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/ping/
diagnostic.emrlite.xray.cancel-url=${diagnostic.emrlite.xray.base-url}/api/v1/integrations/orders/cancel/
diagnostic.emrlite.xray.username=${DIAGNOSTIC_EMRLITE_XRAY_USERNAME}
diagnostic.emrlite.xray.password=${DIAGNOSTIC_EMRLITE_XRAY_PASSWORD}

Expand All @@ -74,6 +79,7 @@ diagnostic.emrlite.truenat.refresh-url=${diagnostic.emrlite.truenat.base-url}/ap
diagnostic.emrlite.truenat.order-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/
diagnostic.emrlite.truenat.result-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/result/
diagnostic.emrlite.truenat.ping-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/ping/
diagnostic.emrlite.truenat.cancel-url=${diagnostic.emrlite.truenat.base-url}/api/v1/integrations/orders/cancel/
diagnostic.emrlite.truenat.username=${DIAGNOSTIC_EMRLITE_TRUENAT_USERNAME}
diagnostic.emrlite.truenat.password=${DIAGNOSTIC_EMRLITE_TRUENAT_PASSWORD}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ public DiagnosticOrderController(DiagnosticOrderService diagnosticOrderService)

@PostMapping("/order/push")
@Operation(summary = "Push a diagnostic order to the configured provider")
public String pushOrder(@RequestBody @Valid DiagnosticOrderRequestDto request) {
public String pushOrder(@RequestBody @Valid DiagnosticOrderRequestDto request,
@RequestHeader("JwtToken") String jwtToken) {
OutputResponse response = new OutputResponse();
try {
DiagnosticOrder order = diagnosticOrderService.createAndPushOrder(request);
DiagnosticOrder order = diagnosticOrderService.createAndPushOrderByUser(request, jwtToken);
response.setResponse(new Gson().toJson(order));
} catch (Exception e) {
logger.error("Error in pushOrder: {}", e.getMessage());
Expand Down Expand Up @@ -128,14 +129,17 @@ public String pollOrder(@RequestParam Long beneficiaryId, @RequestParam String o
}

@PostMapping("/order/manualResult")
@Operation(summary = "Manually submit a diagnostic result for a beneficiary's latest order of the given "
+ "type, for use when no vendor device is integrated. Stored as-is; tbPresence, tbConfidence, and "
+ "drugResistancePresence are left null since nothing derives them. Rejected if that order is "
+ "already COMPLETED.")
public String submitManualResult(@RequestBody @Valid ManualDiagnosticResultRequestDto request) {
@Operation(summary = "Exactly one of resultSummary/reasonToClose is required. resultSummary: manually submit "
+ "a diagnostic result for a beneficiary's latest order of the given type, for use when no vendor "
+ "device is integrated. Stored as-is; tbPresence, tbConfidence, and drugResistancePresence are left "
+ "null since nothing derives them. Rejected if that order is already COMPLETED. reasonToClose: close "
+ "the order instead (same behavior as reasonToClose on /order/push), sourcing patient/orderEvent "
+ "from the beneficiary's existing order for this type.")
public String submitManualResult(@RequestBody @Valid ManualDiagnosticResultRequestDto request,
@RequestHeader("JwtToken") String jwtToken) {
OutputResponse response = new OutputResponse();
try {
DiagnosticOrderResultDto result = diagnosticOrderService.submitManualResult(request);
DiagnosticOrderResultDto result = diagnosticOrderService.submitManualResult(request, jwtToken);
response.setResponse(new GsonBuilder().serializeNulls().create().toJson(result));
} catch (Exception e) {
logger.error("Error in submitManualResult: {}", e.getMessage());
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/com/iemr/flw/domain/iemr/DiagnosticOrder.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
import java.sql.Timestamp;

@Entity
@Table(name = "tb_diagnostic_order", schema = "db_iemr",
uniqueConstraints = @UniqueConstraint(columnNames = {"beneficiary_id", "visitCode", "order_type"}))
@Table(name = "tb_diagnostic_order", schema = "db_iemr")
@Data
public class DiagnosticOrder {

Expand Down Expand Up @@ -53,12 +52,15 @@ public class DiagnosticOrder {
@Column(name = "error_message", columnDefinition = "TEXT")
private String errorMessage;

@Column(name = "reason_for_refusal", columnDefinition = "TEXT")
private String reasonForRefusal;
@Column(name = "reason_to_close", columnDefinition = "TEXT")
private String reasonToClose;

@Column(name = "push_response_json", columnDefinition = "LONGTEXT")
private String pushResponseJson;

@Column(name = "cancel_response_json", columnDefinition = "LONGTEXT")
private String cancelResponseJson;

@Column(name = "patient_first_name", length = 100)
private String patientFirstName;

Expand All @@ -82,6 +84,9 @@ public class DiagnosticOrder {
@Column(name = "modified_by")
private String modifiedBy;

@Column(name = "manually_entered_by")
private String manuallyEnteredBy;

@UpdateTimestamp
@Column(name = "last_mod_date")
private Timestamp lastModDate;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.domain.iemr;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import java.sql.Timestamp;

/**
* One row per applied {@code com.iemr.flw.seeder.migration.FormStructureMigration}, keyed by its
* stable migrationId — lets {@code DynamicFormMigrationRunner} skip a migration it has already run.
*/
@Entity
@Table(name = "t_dynamic_form_migration_log", schema = "db_iemr")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DynamicFormMigrationLog {

@Id
@Column(name = "migrationId", length = 50)
private String migrationId;

@CreationTimestamp
@Column(name = "appliedAt", nullable = false, updatable = false)
private Timestamp appliedAt;
}
5 changes: 5 additions & 0 deletions src/main/java/com/iemr/flw/domain/iemr/FormSection.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ public class FormSection {
@Column(name = "isEditable", nullable = false)
private Boolean isEditable = false;

/** Soft-delete flag: false means removed from its version. Never hard-deleted — historical
* section responses reference it. */
@Column(name = "isActive", nullable = false)
private Boolean isActive = true;

@Column(name = "created_by", length = 100)
private String createdBy;

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/iemr/flw/domain/iemr/FormVersion.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public class FormVersion {
@Column(name = "isLatest", nullable = false)
private Boolean isLatest = true;

/** Soft-delete flag: false means the version is retired. Never hard-deleted — historical
* form responses reference their version. */
@Column(name = "isActive", nullable = false)
private Boolean isActive = true;

@Column(name = "createdAt", nullable = false, updatable = false)
private Timestamp createdAt;

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/iemr/flw/domain/iemr/OptionCondition.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public class OptionCondition {
@ToString.Exclude
private FormSection targetSection;

/** Soft-delete flag: false means removed from its option. Never hard-deleted. */
@Column(name = "isActive", nullable = false)
private Boolean isActive = true;

@Column(name = "created_by", length = 100)
private String createdBy;

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/iemr/flw/domain/iemr/QuestionOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public class QuestionOption {
@Column(name = "displayOrder", nullable = false)
private Integer displayOrder;

/** Soft-delete flag: false means removed from its question. Never hard-deleted — historical
* QuestionResponse rows reference optionId as a plain Long outside JPA's cascade graph. */
@Column(name = "isActive", nullable = false)
private Boolean isActive = true;

@Column(name = "created_by", length = 100)
private String createdBy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public class QuestionValidation {
@Column(name = "errorMessage", nullable = false, length = 500)
private String errorMessage;

/** Soft-delete flag: false means removed from its question. Never hard-deleted. */
@Column(name = "isActive", nullable = false)
private Boolean isActive = true;

@Column(name = "created_by", length = 100)
private String createdBy;

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/iemr/flw/domain/iemr/SectionQuestion.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ public class SectionQuestion {
@Column(name = "containsPii", nullable = false)
private Boolean containsPii = false;

/** Soft-delete flag: false means unlinked from its section. Never hard-deleted — historical
* QuestionResponse rows reference questionId as a plain Long outside JPA's cascade graph. */
@Column(name = "isActive", nullable = false)
private Boolean isActive = true;

@Column(name = "created_by", length = 100)
private String createdBy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,10 @@ public class StopTBGeneralExamination {
@Column(name = "clubbing")
private String clubbing;

// JSON arrays serialised from mobile
@Column(name = "key_population_risk_factor_ids", columnDefinition = "TEXT")
private String keyPopulationRiskFactorIds;

@Column(name = "key_population_risk_factors", columnDefinition = "TEXT")
private String keyPopulationRiskFactors;

@Column(name = "hiv_status_id")
private Integer hivStatusId;

// "Positive" | "Reactive" | "Negative" | "Unknown"
@Column(name = "hiv_status")
private String hivStatus;
// key_population_risk_factor_ids/_factors and hiv_status_id/hiv_status were removed from
// this table — TB Screening (TBScreening) is the single source of truth for these fields
// (see StopTBServiceImpl#examToMap, and the AMRIT-DB migration that backfilled tb_screening
// from this table's historical data before dropping the columns here).

@Column(name = "referral_to_hwc_needed_id")
private Integer referralToHWCNeededId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ public class DiagnosticOrderRequestDto {
@NotNull
private Long beneficiaryId;

@NotNull
private Long visitCode;

@NotNull
private String orderType;

private String orderEvent;

private String reasonForRefusal;
private String reasonToClose;

@NotNull
@Valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class DiagnosticOrderResultDto {
private String providerStatus;
private String resultSummary;
private String errorMessage;
private String reasonForRefusal;
private String reasonToClose;
private Boolean tbPresence;
private Double tbConfidence;
private Boolean drugResistancePresence;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
public class DiagnosticOrderStatusSummaryDto {
private List<Long> awaitingProviderResult;
private List<Long> completed;
private List<Long> pollingTimedOut;
private List<Long> failed;
private List<Long> refused;
private List<Long> closed;
private List<Long> awaitingManualEntry;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.iemr.flw.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand All @@ -15,6 +14,7 @@ public class ManualDiagnosticResultRequestDto {
@NotNull
private String orderType;

@NotBlank
private String resultSummary;

private String reasonToClose;
}
6 changes: 6 additions & 0 deletions src/main/java/com/iemr/flw/dto/iemr/DynamicFormDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/
package com.iemr.flw.dto.iemr;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.iemr.flw.masterEnum.FormType;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
Expand Down Expand Up @@ -62,4 +63,9 @@ public class DynamicFormDTO {

@Valid
private List<FormSectionDTO> sections = new ArrayList<>();

/** Every version's full definition, oldest first — populated only by getAllForms (the top-level
* fields above stay the latest version); omitted from the JSON elsewhere and ignored on write. */
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<DynamicFormDTO> versions;
}
2 changes: 1 addition & 1 deletion src/main/java/com/iemr/flw/dto/iemr/FormSectionDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class FormSectionDTO {

private Boolean isRequired = true;

@NotNull(message = "displayOrder is required")
/** Omit to auto-append after existing siblings; set explicitly to insert at that position (existing siblings shift up). */
private Integer displayOrder;

private Boolean hasSubmitButton = false;
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/com/iemr/flw/dto/iemr/QuestionOptionDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand Down Expand Up @@ -51,7 +50,7 @@ public class QuestionOptionDTO {

private String optionValueHindi;

@NotNull(message = "displayOrder is required")
/** Omit to auto-append after existing siblings; set explicitly to insert at that position (existing siblings shift up). */
private Integer displayOrder;

@Valid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class SectionQuestionDTO {

private Boolean isMandatory = true;

@NotNull(message = "displayOrder is required")
/** Omit to auto-append after existing siblings; set explicitly to insert at (or move to) that position (existing siblings shift). */
private Integer displayOrder;

private Integer maxLength;
Expand Down
Loading
Loading