Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d128570
minor changes
emmannajichi Jun 30, 2026
e299356
Merge branch 'new-tool-hts' into dev-new-gam-24
emmannajichi Jun 30, 2026
fa0072d
resolved feedbacks around relaxing 6 mandatory fields in knowledge as…
emmannajichi Jun 30, 2026
2f8eca3
minor changes
emmannajichi Jun 30, 2026
fd7a879
starting diagnostic testing
emmannajichi Jul 1, 2026
db06a20
done with jira issues
emmannajichi Jul 1, 2026
4de0e3c
add a fi for completed by and desgniantion
emmannajichi Jul 6, 2026
b63e94b
added ability to search by client code
emmannajichi Jul 6, 2026
0910512
fix: optimized hts grids
gamalieldashuaDataFi Jul 6, 2026
9a51b09
fix: optimized hts grids
gamalieldashuaDataFi Jul 6, 2026
80bc11d
fix: added some cleanups
gamalieldashuaDataFi Jul 6, 2026
13329e3
more issues solved
emmannajichi Jul 6, 2026
0e37a5a
solved the update issue
emmannajichi Jul 7, 2026
e5639af
worked up to pre realease
emmannajichi Jul 8, 2026
2a2b284
worked up to pre realease
emmannajichi Jul 8, 2026
8bf2f46
Update package.json
gamalieldashuaDataFi Jul 8, 2026
9f24854
minor issue to relax special characters for serialNumber
emmannajichi Jul 9, 2026
4121c3f
Merge branch 'dev-new-gam-24' of https://github.com/lamisplus/HTS-Mod…
emmannajichi Jul 9, 2026
a19dd72
minor change on package.json
emmannajichi Jul 9, 2026
b50a26e
added search hts patients by client code
emmannajichi Jul 14, 2026
e406e2e
adjusted changeset to the proper version reslving a bug
emmannajichi Jul 19, 2026
dca29cc
made minor changes to resolve a changeset issue
emmannajichi Jul 22, 2026
73e2914
finally slved the issue
emmannajichi Jul 23, 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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<groupId>org.lamisplus.modules</groupId>
<artifactId>hts</artifactId>
<version>2.5.0.2</version>
<version>2.5.1</version>
<packaging>jar</packaging>

<name>Hts</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import javax.validation.Valid;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/api/v1/hts-encounter")
Expand Down Expand Up @@ -86,7 +85,9 @@ public ResponseEntity<PageDTO> getPatientSummaries(
@PreAuthorize("hasAnyAuthority('hts_view', 'hts_encounter_view')")
public ResponseEntity<HtsEncounterResponse> getForProphylaxis(
@PathVariable("screening-date") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate screeningDate,
@PathVariable("patient-uuid") UUID patientUuid) {
// patientUuid taken as a raw String (not java.util.UUID) so legacy/migrated
// patient_uuid values that aren't well-formed UUIDs don't 400 at binding time.
@PathVariable("patient-uuid") String patientUuid) {
return ResponseEntity.ok(service.getForProphylaxis(screeningDate, patientUuid));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,12 @@ public class HtsEncounterRequest {
private String complaintsScroralSwelling;
private String complaintsGenitalSores;
private String complaintsSwollenLymphNodes;

private String partnerNewlyDiagnosed;
private String partnerPregnantOnArv;
private String adolescentHivPositive;
private String partnerNotRegularlyOnDrugs;
private String partnerRecentlyReturnedToTreatment;
private String hadSexWithHivPositivePartnerInRiskGroup;

private String typeOfHivTestDone;
private String initialHivTest;
private String suspectedAcuteInfection;
Expand All @@ -85,7 +83,6 @@ public class HtsEncounterRequest {
private String syphilisTestResult;
private String recencyTest;
private String hivEarlyDetectResult;

private String previouslyTestedThisYear;
private String clientReceivedTestResult;
private String hivTestKitsProvided;
Expand All @@ -100,7 +97,6 @@ public class HtsEncounterRequest {
private String completedBy;
private String designation;
private Boolean pmtctHts = false;

private String source ="Web";
private String longitude;
private String latitude;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
import org.lamisplus.modules.patient.domain.dto.PersonResponseDto;

import java.time.LocalDate;
import java.util.UUID;

@Data
public class HtsEncounterResponse {
private Long id;
private String uuid;
private Long patientId;
private UUID patientUuid;
private String patientUuid;
private PersonResponseDto person;
private String clientCode;
private LocalDate dateOfVisit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.UUID;

@Entity
@Table(name = "hts_encounter")
Expand All @@ -36,8 +35,9 @@ public class HtsEncounter extends Audit<HtsEncounter> implements Serializable {
@JoinColumn(name = "patient_id", nullable = false)
private Person person;

@Column(name = "patient_uuid", columnDefinition = "uuid")
private UUID patientUuid;

@Column(name = "patient_uuid", columnDefinition = "varchar(50)", length = 50)
private String patientUuid;

@Column(name = "client_code", nullable = false, length = 50)
private String clientCode;
Expand Down Expand Up @@ -71,4 +71,4 @@ public class HtsEncounter extends Audit<HtsEncounter> implements Serializable {

@Column(nullable = false)
private Boolean archived = false;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package org.lamisplus.modules.hts.installers;

import com.foreach.across.core.annotations.Installer;
import com.foreach.across.core.installers.AcrossLiquibaseInstaller;
import org.springframework.core.annotation.Order;

@Order(23)
@Installer(name = "harmonize-new-hts-tables-new",
@Installer(name = "harmonize-new-hts-tables-new-bug-resolved",
description = "Resolves divergent HTS tables",
version = 15)
version = 18)
public class HarmoniseNewHtsTablesInstaller extends AcrossLiquibaseInstaller {
public HarmoniseNewHtsTablesInstaller() {
super("classpath:installers/hts/harmonise_new_hts_tables.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import org.springframework.core.annotation.Order;

@Order(20)
@Installer(name = "hts-encounter-schema-installer-new-again",
@Installer(name = "hts-encounter-schema-installer-new-again-bug-resolved",
description = "Creates the hts_encounter table — V3: boolean archived, patient_id, patient_uuid, facility_uuid, observation column",
version = 43)
version = 44)
public class HtsEncounterSchemaInstaller extends AcrossLiquibaseInstaller {
public HtsEncounterSchemaInstaller() {
super("classpath:installers/hts/encounter_schema.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import org.springframework.core.annotation.Order;

@Order(21)
@Installer(name = "ict-encounter-schema-installer-new-again",
@Installer(name = "ict-encounter-schema-installer-new-again-bug-resolved",
description = "Creates ict_encounter and ict_contact tables — V3: boolean archived, patient_id, patient_uuid, facility_uuid, contact renames, removed age columns",
version = 43)
version = 44)
public class IctEncounterSchemaInstaller extends AcrossLiquibaseInstaller {
public IctEncounterSchemaInstaller() {
super("classpath:installers/hts/ict_encounter_schema.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

@Order(3)
@Installer(name = "schema-installer-update", description = "Updates the required database tables data",
version = 15)
version = 18)
public class Updates extends AcrossLiquibaseInstaller {
public Updates() {
super("classpath:installers/hts/updates.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,30 +34,40 @@ public interface HtsClientRepository extends JpaRepository<HtsClient, Long> {
Optional<HtsClient> findFirstByRiskStratificationCode(String riskStratificationCode);


@Query(value = "SELECT \n" +
" p.hospital_number AS hospitalNumber,\n" +
" p.id AS personId,\n" +
" p.uuid AS personUuid,\n" +
" p.first_name AS firstName,\n" +
" p.surname AS surname,\n" +
" p.other_name AS otherName,\n" +
" CAST(EXTRACT(YEAR FROM AGE(NOW(), p.date_of_birth)) AS INTEGER) AS age,\n" +
" INITCAP(p.sex) AS gender\n" +
"FROM patient_person p\n" +
"WHERE p.archived = ?1\n" +
"AND (\n" +
" p.first_name ILIKE ?3\n" +
" OR p.surname ILIKE ?3\n" +
" OR p.other_name ILIKE ?3\n" +
" OR p.hospital_number ILIKE ?3\n" +
" OR p.contact_point#>>'{contactPoint,0,value}' ILIKE ?3\n" +
")\n" +
@Query(value = "SELECT hc.client_code as clientCode, p.id as personId, p.first_name as firstName, p.surname as surname, p.other_name as otherName, " +
"p.hospital_number as hospitalNumber, CAST (EXTRACT(YEAR from AGE(NOW(), date_of_birth)) AS INTEGER) as age, " +
"INITCAP(p.sex) as gender, p.date_of_birth as dateOfBirth, CAST (COUNT(hc.person_uuid) AS INTEGER) as htsCount " +
"FROM patient_person p " +
"LEFT JOIN hts_client hc ON hc.person_uuid = p.uuid AND hc.archived = ?1 " +
"WHERE p.archived=?1 AND p.facility_id=?2 AND (p.first_name ILIKE ?3 " +
"OR p.surname ILIKE ?3 OR p.other_name ILIKE ?3 " +
"OR p.hospital_number ILIKE ?3 OR hc.client_code ILIKE ?3) " +
"GROUP BY hc.client_code, p.id, p.first_name, p.first_name, p.surname, p.other_name, p.hospital_number, p.date_of_birth", nativeQuery = true)
List<HtsPerson> findAllPersonHtsBySearchParam(Integer archived, Long facilityId, String search);


String PATIENTS_INFANT_CTE =
"WITH pmtct_infants AS (\n" +
" SELECT DISTINCT hospital_number\n" +
" FROM pmtct_infant_hospital_numbers() AS t(hospital_number)\n" +
")\n";

String PATIENTS_SELECT_COLS =
"SELECT p.hospital_number AS hospitalNumber, p.uuid AS personUuid, p.id AS personId,\n" +
" p.first_name AS firstName, p.surname AS surname, p.other_name AS otherName,\n" +
" CAST(EXTRACT(YEAR FROM AGE(NOW(), p.date_of_birth)) AS INTEGER) AS age,\n" +
" INITCAP(p.sex) AS gender\n" +
"FROM patient_person p\n";

// Two hts_encounter anti-joins — identical to findAllPersonHts — plus the
// set-based infant exclusion replacing is_pmtct_infant(p.hospital_number).
String PATIENTS_NOT_EXISTS =
"AND NOT EXISTS (\n" +
" SELECT 1\n" +
" FROM hts_encounter hs\n" +
" WHERE hs.client_code = p.hospital_number\n" +
" AND hs.archived = false\n" +
" AND CAST(hs.patient_uuid AS TEXT) IS NOT NULL AND CAST(hs.patient_uuid AS TEXT) != ''\n" +
" AND CAST(hs.patient_uuid AS TEXT) is not null AND CAST(hs.patient_uuid AS TEXT) != ''\n" +
" AND hs.facility_id = ?2\n" +
")\n" +
"AND NOT EXISTS (\n" +
Expand All @@ -67,72 +77,93 @@ public interface HtsClientRepository extends JpaRepository<HtsClient, Long> {
" AND htse.facility_id = ?2\n" +
" AND htse.archived = false\n" +
")\n" +
"AND is_pmtct_infant(p.hospital_number) = FALSE\n" +
"", nativeQuery = true)
Page<HtsPerson> findAllPersonHtsBySearchParam(Integer archived, Long facilityId, String search, Pageable pageable);


@Query(value = "SELECT DISTINCT hc.client_code AS clientCode, p.id as personId,p.uuid as personUuid, p.first_name as firstName, p.surname as surname, p.other_name as otherName, \n" +
"p.hospital_number as hospitalNumber, CAST (EXTRACT(YEAR from AGE(NOW(), date_of_birth)) AS INTEGER) as age, \n" +
"INITCAP(p.sex) as gender, p.date_of_birth as dateOfBirth,mini.person_uuid,mini.hts_count htsCount \n" +
"FROM patient_person p \n" +
"INNER JOIN hts_client hc ON hc.person_uuid = p.uuid AND hc.archived = ?1 \n" +
"INNER JOIN (SELECT person_uuid,MIN(date_created) first_hts_registration,COUNT(person_uuid) hts_count \n" +
"FROM hts_client \n" +
"WHERE person_uuid IS NOT NULL AND archived = ?1 AND facility_id=?2 " +
"GROUP BY 1) mini \n" +
"ON hc.person_uuid=mini.person_uuid AND hc.date_created=mini.first_hts_registration \n" +
"where p.first_name ILIKE ?3 OR p.surname ILIKE ?3 OR p.other_name ILIKE ?3 \n" +
"OR p.hospital_number ILIKE ?3 OR hc.client_code ILIKE ?3 ", nativeQuery = true)
Page<HtsPerson> findOnlyPersonHtsBySearchParam(Integer archived, Long facilityId, String search, Pageable pageable);
"AND NOT EXISTS (\n" +
" SELECT 1 FROM pmtct_infants pin WHERE pin.hospital_number = p.hospital_number\n" +
")\n";

@Query(value =
PATIENTS_INFANT_CTE + PATIENTS_SELECT_COLS +
"WHERE p.archived = ?1 AND p.facility_id = ?2\n" +
PATIENTS_NOT_EXISTS +
"ORDER BY p.created_date DESC",
countQuery =
PATIENTS_INFANT_CTE +
"SELECT COUNT(*) FROM patient_person p\n" +
"WHERE p.archived = ?1 AND p.facility_id = ?2\n" +
PATIENTS_NOT_EXISTS,
nativeQuery = true)
Page<HtsPerson> findAllPersonHtsOptimized(Integer archived, Long facilityId, Pageable pageable);

@Query(value = "SELECT hc.client_code as clientCode, p.id as personId, p.first_name as firstName, p.surname as surname, p.other_name as otherName, " +
"p.hospital_number as hospitalNumber, CAST (EXTRACT(YEAR from AGE(NOW(), date_of_birth)) AS INTEGER) as age, " +
"INITCAP(p.sex) as gender, p.date_of_birth as dateOfBirth, CAST (COUNT(hc.person_uuid) AS INTEGER) as htsCount " +
"FROM patient_person p " +
"LEFT JOIN hts_client hc ON hc.person_uuid = p.uuid AND hc.archived = ?1 " +
"WHERE p.archived=?1 AND p.facility_id=?2 AND (p.first_name ILIKE ?3 " +
"OR p.surname ILIKE ?3 OR p.other_name ILIKE ?3 " +
"OR p.hospital_number ILIKE ?3 OR hc.client_code ILIKE ?3) " +
"GROUP BY hc.client_code, p.id, p.first_name, p.first_name, p.surname, p.other_name, p.hospital_number, p.date_of_birth", nativeQuery = true)
List<HtsPerson> findAllPersonHtsBySearchParam(Integer archived, Long facilityId, String search);
@Query(value =
PATIENTS_INFANT_CTE + PATIENTS_SELECT_COLS +
"WHERE p.archived = ?1\n" +
"AND (\n" +
" p.first_name ILIKE ?3\n" +
" OR p.surname ILIKE ?3\n" +
" OR p.other_name ILIKE ?3\n" +
" OR p.hospital_number ILIKE ?3\n" +
" OR p.contact_point#>>'{contactPoint,0,value}' ILIKE ?3\n" +
")\n" +
PATIENTS_NOT_EXISTS,
countQuery =
PATIENTS_INFANT_CTE +
"SELECT COUNT(*) FROM patient_person p\n" +
"WHERE p.archived = ?1\n" +
"AND (\n" +
" p.first_name ILIKE ?3\n" +
" OR p.surname ILIKE ?3\n" +
" OR p.other_name ILIKE ?3\n" +
" OR p.hospital_number ILIKE ?3\n" +
" OR p.contact_point#>>'{contactPoint,0,value}' ILIKE ?3\n" +
")\n" +
PATIENTS_NOT_EXISTS,
nativeQuery = true)
Page<HtsPerson> findAllPersonHtsBySearchParamOptimized(Integer archived, Long facilityId, String search, Pageable pageable);

String ONLY_PERSON_HTS_CTE =
"WITH first_hc AS (\n" +
" SELECT DISTINCT ON (hc.person_uuid)\n" +
" hc.id, hc.client_code, hc.person_uuid, hc.date_created,\n" +
" COUNT(*) OVER (PARTITION BY hc.person_uuid) AS hts_count\n" +
" FROM hts_client hc\n" +
" WHERE hc.person_uuid IS NOT NULL\n" +
" AND hc.archived = ?1\n" +
" AND hc.facility_id = ?2\n" +
" ORDER BY hc.person_uuid, hc.date_created ASC, hc.id ASC\n" +
")\n";

String ONLY_PERSON_HTS_SELECT =
"SELECT fc.id, fc.client_code AS clientCode, p.id as personId, p.uuid as personUuid,\n" +
" p.first_name as firstName, p.surname as surname, p.other_name as otherName,\n" +
" p.hospital_number as hospitalNumber,\n" +
" CAST(EXTRACT(YEAR from AGE(NOW(), p.date_of_birth)) AS INTEGER) as age,\n" +
" INITCAP(p.sex) as gender, p.date_of_birth as dateOfBirth,\n" +
" fc.person_uuid, fc.hts_count htsCount\n" +
"FROM first_hc fc\n" +
"INNER JOIN patient_person p ON p.uuid = fc.person_uuid\n";

@Query(value = "SELECT p.hospital_number AS hospitalNumber, p.uuid as personUuid, p.id AS personId, p.first_name AS firstName, p.surname AS surname, \n" +
" p.other_name AS otherName, CAST(EXTRACT(YEAR FROM AGE(NOW(), p.date_of_birth)) AS INTEGER) AS age, INITCAP(p.sex) AS gender \n" +
"\tFROM patient_person p \n" +
"\tWHERE p.archived = ?1 AND p.facility_id = ?2 \n" +
"\t\t\t\tAND NOT EXISTS (\n" +
"\t\t\t\t\tSELECT 1 \n" +
"\t\t\t\t\tFROM hts_encounter hs\n" +
"\t\t\t\t\tWHERE hs.client_code = p.hospital_number\n" +
"\t\t\t\t\tAND hs.archived = false \n" +
"\t\t\t\t\tAND CAST(hs.patient_uuid AS TEXT) is not null AND CAST(hs.patient_uuid AS TEXT) != ''\n" +
"\t\t\t\t\tAND hs.facility_id = ?2 \n" +
"\t\t\t\t)\n" +
" AND NOT EXISTS (\n" +
"\t\t\t\t SELECT 1\n" +
"\t\t\t\t FROM hts_encounter htse\n" +
"\t\t\t\t WHERE CAST(htse.patient_uuid AS TEXT) = p.uuid\n" +
"\t\t\t\t AND htse.facility_id = ?2\n" +
"\t\t\t\t AND htse.archived = false\n" +
" )\n" +
" AND is_pmtct_infant(p.hospital_number) = FALSE\n" +
"\t\t\tORDER BY p.created_date DESC" , nativeQuery = true)
Page<HtsPerson> findAllPersonHts(Integer archived, Long facilityId, Pageable pageable);
@Query(value = "SELECT DISTINCT hc.id, hc.client_code AS clientCode, p.id as personId,p.uuid as personUuid, p.first_name as firstName, p.surname as surname, p.other_name as otherName, \n" +
"p.hospital_number as hospitalNumber, CAST (EXTRACT(YEAR from AGE(NOW(), date_of_birth)) AS INTEGER) as age, \n" +
"INITCAP(p.sex) as gender, p.date_of_birth as dateOfBirth,mini.person_uuid,mini.hts_count htsCount\n" +
"FROM patient_person p\n" +
"INNER JOIN hts_client hc ON hc.person_uuid = p.uuid AND hc.archived = ?1 \n" +
"INNER JOIN (SELECT person_uuid,MIN(date_created) first_hts_registration,COUNT(person_uuid) hts_count\n" +
"FROM hts_client\n" +
"WHERE person_uuid IS NOT NULL AND archived = ?1 AND facility_id=?2 \n" +
"GROUP BY 1) mini\n" +
"ON hc.person_uuid=mini.person_uuid AND hc.date_created=mini.first_hts_registration ORDER BY hc.id DESC", nativeQuery = true)
Page<HtsPerson> findOnlyPersonHts(Integer archived, Long facilityId, Pageable pageable);
@Query(value =
ONLY_PERSON_HTS_CTE + ONLY_PERSON_HTS_SELECT + "ORDER BY fc.id DESC",
countQuery =
ONLY_PERSON_HTS_CTE +
"SELECT COUNT(*) FROM first_hc fc " +
"INNER JOIN patient_person p ON p.uuid = fc.person_uuid",
nativeQuery = true)
Page<HtsPerson> findOnlyPersonHtsOptimized(Integer archived, Long facilityId, Pageable pageable);

@Query(value =
ONLY_PERSON_HTS_CTE + ONLY_PERSON_HTS_SELECT +
"WHERE p.first_name ILIKE ?3 OR p.surname ILIKE ?3 OR p.other_name ILIKE ?3\n" +
" OR p.hospital_number ILIKE ?3 OR fc.client_code ILIKE ?3\n" +
"ORDER BY fc.id DESC",
countQuery =
ONLY_PERSON_HTS_CTE +
"SELECT COUNT(*) FROM first_hc fc " +
"INNER JOIN patient_person p ON p.uuid = fc.person_uuid " +
"WHERE p.first_name ILIKE ?3 OR p.surname ILIKE ?3 OR p.other_name ILIKE ?3 " +
" OR p.hospital_number ILIKE ?3 OR fc.client_code ILIKE ?3",
nativeQuery = true)
Page<HtsPerson> findOnlyPersonHtsBySearchParamOptimized(Integer archived, Long facilityId, String search, Pageable pageable);

@Query(value = "WITH hts_patients AS ( \n" +
" SELECT DISTINCT \n" +
Expand Down
Loading