Skip to content
Merged
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
d07422d
fix: amm-2368 item to shop mapping issue
Jul 1, 2026
8d9ba06
Merge pull request #137 from PSMRI/sn/itemMap
snehar-nd Jul 1, 2026
931c56b
Fix the Health Response Format (#138)
vanitha1822 Jul 14, 2026
235e63e
Vb/release 3.8.2 merge (#139)
vishwab1 Jul 28, 2026
f78178c
fix: Stop TB bulk user upload district validation and qualification c…
vishwab1 Aug 3, 2026
bf251a4
Merge pull request #140 from PSMRI/vb/release-3.8.2-fix
SauravBizbRolly Aug 3, 2026
28ffe68
fix update user issue and role issue (#143)
SauravBizbRolly Aug 5, 2026
c9e1d85
Fix/admin bulk registration role issue (#144)
SauravBizbRolly Aug 5, 2026
c6ada0a
USER ID / Login ID Update for Mobile Number Change (#146)
snehar-nd Sep 3, 2026
de57d4f
Role Mapping Not Saving When Updating Multiple Roles (Registrar/Nurse…
vishwab1 Sep 11, 2026
ef9d827
perf(nikshay): return only id/name/parent from TU, facility and villa…
vishwab1 Sep 28, 2026
0a93e84
feat: add villageMapping/updateVillage endpoint
Sep 28, 2026
b45453f
Merge release-3.9.1 into sn/update-village
Sep 30, 2026
f24f4ff
feat: move all beneficiary addresses when user has a single village
Sep 30, 2026
948c6ad
Merge pull request #153 from PSMRI/sn/update-village
snehar-nd Sep 30, 2026
d81e65e
perf(nikshay): read only id/name/facility for the villages lookup
vishwab1 Oct 9, 2026
1184e9f
feat: AMM-2469, 2352, 2415, bulk registration API improvements. (#154)
chetansaroya Oct 13, 2026
ba5245d
Merge origin/release-3.9.1 into vb/release-3.8.4-nikshay-perf
vishwab1 Oct 14, 2026
2970b56
feat(stoptb): team (MMU van) mapping API for Work Location Mapping (A…
vishwab1 Oct 14, 2026
eed7768
feat(stoptb): store the team as VanID on the work location mapping (A…
vishwab1 Oct 14, 2026
46e4bd5
Merge origin/release-3.12.0 into vb/stoptb-bulk-upload-fixes
vishwab1 Oct 14, 2026
2dd2336
Merge pull request #155 from PSMRI/vb/stoptb-bulk-upload-fixes
SauravBizbRolly Oct 21, 2026
196deb8
feat(AMM-2491): Min Stock / Max Stock in Item Master (#156)
vishwab1 Oct 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 @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.iemr.admin</groupId>
<artifactId>admin-api</artifactId>
<version>3.8.2</version>
<version>3.12.0</version>
<packaging>war</packaging>
<name>Admin-API</name>
<description>Admin Page</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,12 @@ public String UserRoleMappings(@RequestBody String userRoleMapping, HttpServletR
Previleges1097_3[] predata1 = pre.get(x).getPrevileges();
for (Previleges1097_3 previl : predata1) {

// Each role in the ID array must become its own saved row. This used to
// build a single M_UserServiceRoleMapping2 across all iterations of this
// loop and only add it to resList1 once, after the loop — so a batched
// request carrying multiple roles (e.g. Nurse + Counsellor) silently
// discarded every role but the last one. Moving the object creation and
// the add() call inside the loop saves one row per role, as intended.
Priveleges1097_2[] predata2 = previl.getID();
for (Priveleges1097_2 previl1 : predata2) {
resDataMap1 = new M_UserServiceRoleMapping2();
Expand All @@ -1800,22 +1806,23 @@ public String UserRoleMappings(@RequestBody String userRoleMapping, HttpServletR
if (previl1.getTeleConsultation() != null) {
resDataMap1.setTeleConsultation(previl1.getTeleConsultation());
}
resDataMap1.setUserID(employeeMaster.get(x).getUserID());
resDataMap1.setProviderServiceMapID(previl.getProviderServiceMapID());
resDataMap1.setWorkingLocationID(previl.getWorkingLocationID());
resDataMap1.setStateID(previl.getStateID());
resDataMap1.setDistrictID(previl.getDistrictID());
resDataMap1.setCreatedBy(employeeMaster.get(x).getCreatedBy());
resDataMap1.setServiceProviderID(employeeMaster.get(x).getServiceProviderID());
resDataMap1.setBlockID(previl.getBlockID());
resDataMap1.setBlockName(previl.getBlockName());
resDataMap1.setVillageID(previl.getVillageID());
resDataMap1.setVillageName(previl.getVillageName());
resDataMap1.setFacilityID(previl.getFacilityID());
resDataMap1.setNikshayTUID(previl.getNikshayTUID());
resDataMap1.setNikshayFacilityID(previl.getNikshayFacilityID());
resDataMap1.setVanID(previl.getVanID());
resList1.add(resDataMap1);
}
resDataMap1.setUserID(employeeMaster.get(x).getUserID());
resDataMap1.setProviderServiceMapID(previl.getProviderServiceMapID());
resDataMap1.setWorkingLocationID(previl.getWorkingLocationID());
resDataMap1.setStateID(previl.getStateID());
resDataMap1.setDistrictID(previl.getDistrictID());
resDataMap1.setCreatedBy(employeeMaster.get(x).getCreatedBy());
resDataMap1.setServiceProviderID(employeeMaster.get(x).getServiceProviderID());
resDataMap1.setBlockID(previl.getBlockID());
resDataMap1.setBlockName(previl.getBlockName());
resDataMap1.setVillageID(previl.getVillageID());
resDataMap1.setVillageName(previl.getVillageName());
resDataMap1.setFacilityID(previl.getFacilityID());
resDataMap1.setNikshayTUID(previl.getNikshayTUID());
resDataMap1.setNikshayFacilityID(previl.getNikshayFacilityID());
resList1.add(resDataMap1);

}
x++;
Expand Down Expand Up @@ -1863,11 +1870,15 @@ public String updateUserRoleMapping(@RequestBody String updateUserRoleMapping, H
}
}

// Soft-delete other active mappings for same user+service to prevent duplicates
// (e.g. old roleID 122 rows left over when new roleID 128 mapping was created)
if (pre.getUserID() != null && pre.getProviderServiceMapID() != null && pre.getuSRMappingID() != null) {
// Soft-delete other active mappings for same user+service+role to prevent
// duplicates (e.g. a stale leftover row still claiming this same role).
// Scoped by roleID as well as user+service — otherwise this deletes every
// OTHER role the user holds under this service line too, not just true
// duplicates of the role being saved here.
if (pre.getUserID() != null && pre.getProviderServiceMapID() != null && pre.getRoleID() != null
&& pre.getuSRMappingID() != null) {
employeeMasterInter.softDeleteOldMappings(
pre.getUserID(), pre.getProviderServiceMapID(), pre.getuSRMappingID());
pre.getUserID(), pre.getProviderServiceMapID(), pre.getRoleID(), pre.getuSRMappingID());
}

usrRole.setUserID(pre.getUserID());
Expand All @@ -1885,6 +1896,7 @@ public String updateUserRoleMapping(@RequestBody String updateUserRoleMapping, H
usrRole.setFacilityID(pre.getFacilityID());
usrRole.setNikshayTUID(pre.getNikshayTUID());
usrRole.setNikshayFacilityID(pre.getNikshayFacilityID());
usrRole.setVanID(pre.getVanID());

if (pre.getTeleConsultation() != null) {
usrRole.setTeleConsultation(pre.getTeleConsultation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ public String editItemMaster(@RequestBody ItemMaster item) {
saveData.setItemDesc(item.getItemDesc());
saveData.setSctCode(item.getSctCode());
saveData.setSctTerm(item.getSctTerm());
// Only when sent, so screens that don't show these fields can't wipe them (0 clears the threshold)
if (item.getMinStock() != null)
saveData.setMinStock(item.getMinStock());
if (item.getMaxStock() != null)
saveData.setMaxStock(item.getMaxStock());
saveData.setModifiedBy(item.getModifiedBy());
saveData = itemService.createItemMaster(saveData);
response.setResponse(saveData.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand All @@ -40,7 +41,6 @@
import com.iemr.admin.data.nikshay.NikshayFacility;
import com.iemr.admin.data.nikshay.NikshayState;
import com.iemr.admin.data.nikshay.NikshayTU;
import com.iemr.admin.data.nikshay.NikshayVillage;
import com.iemr.admin.repo.employeemaster.EmployeeMasterRepo;
import com.iemr.admin.repo.nikshay.NikshayDistrictRepo;
import com.iemr.admin.repo.nikshay.NikshayFacilityRepo;
Expand Down Expand Up @@ -121,7 +121,7 @@
OutputResponse response = new OutputResponse();
try {
List<NikshayTU> tus = nikshayTURepo.findByDistrictID(districtID);
response.setResponse(tus.toString());
response.setResponse(new Gson().toJson(tus.stream().map(this::toLite).collect(Collectors.toList())));

Check warning on line 124 in src/main/java/com/iemr/admin/controller/nikshay/NikshayLocationController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWgc0NW0YKJTPbK5&open=AaGbnWgc0NW0YKJTPbK5&pullRequest=160
} catch (Exception e) {
logger.error("Error fetching Nikshay TUs for districtID " + districtID + ": " + e.getMessage(), e);
response.setError(e);
Expand All @@ -136,7 +136,7 @@
try {
List<Integer> ids = parseIntCsv(tuIDs);
List<NikshayFacility> facilities = nikshayFacilityRepo.findByTUIDs(ids);
response.setResponse(facilities.toString());
response.setResponse(new Gson().toJson(facilities.stream().map(this::toLite).collect(Collectors.toList())));

Check warning on line 139 in src/main/java/com/iemr/admin/controller/nikshay/NikshayLocationController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWgc0NW0YKJTPbK6&open=AaGbnWgc0NW0YKJTPbK6&pullRequest=160
} catch (Exception e) {
logger.error("Error fetching Nikshay facilities for tuIDs " + tuIDs + ": " + e.getMessage(), e);
response.setError(e);
Expand All @@ -150,8 +150,8 @@
OutputResponse response = new OutputResponse();
try {
List<Integer> ids = parseIntCsv(facilityIDs);
List<NikshayVillage> villages = nikshayVillageRepo.findByFacilityIDs(ids);
response.setResponse(villages.toString());
List<Object[]> villages = nikshayVillageRepo.findLiteByFacilityIDs(ids);
response.setResponse(new Gson().toJson(villages.stream().map(this::toLiteVillage).collect(Collectors.toList())));

Check warning on line 154 in src/main/java/com/iemr/admin/controller/nikshay/NikshayLocationController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWgc0NW0YKJTPbK7&open=AaGbnWgc0NW0YKJTPbK7&pullRequest=160
} catch (Exception e) {
logger.error("Error fetching villages for facilityIDs " + facilityIDs + ": " + e.getMessage(), e);
response.setError(e);
Expand All @@ -172,8 +172,9 @@
Map<String, Object> result = new HashMap<>();
if (row != null) {
result.put("districtID", row.getDistrictID());
result.put("nikshayTUID", row.getNikshayTUID());

Check failure on line 175 in src/main/java/com/iemr/admin/controller/nikshay/NikshayLocationController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

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

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWgc0NW0YKJTPbK3&open=AaGbnWgc0NW0YKJTPbK3&pullRequest=160
result.put("nikshayFacilityID", row.getNikshayFacilityID());

Check failure on line 176 in src/main/java/com/iemr/admin/controller/nikshay/NikshayLocationController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

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

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWgc0NW0YKJTPbK4&open=AaGbnWgc0NW0YKJTPbK4&pullRequest=160
result.put("vanID", row.getVanID());
}
response.setResponse(new Gson().toJson(result));
} catch (Exception e) {
Expand All @@ -183,6 +184,37 @@
return response.toString();
}

// TU/Facility/Village lists can run into thousands of rows for a single
// district (e.g. Pune MC: 5,327 facilities), and the full entities carry
// audit fields (createdBy/createdDate/modifiedBy/lastModDate/codes) the
// Admin UI never reads. Send only the ID, name and parent ID each dropdown
// needs — same field names as before, so older UI builds keep working.
private Map<String, Object> toLite(NikshayTU t) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("nikshayTUID", t.getNikshayTUID());
m.put("tUName", t.getTUName());
m.put("nikshayDistrictID", t.getNikshayDistrictID());
return m;
}

private Map<String, Object> toLite(NikshayFacility f) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("nikshayFacilityID", f.getNikshayFacilityID());
m.put("facilityName", f.getFacilityName());
m.put("nikshayTUID", f.getNikshayTUID());
return m;
}

// row = [nikshayVillageID, villageName, nikshayFacilityID], see
// NikshayVillageRepo.findLiteByFacilityIDs
private Map<String, Object> toLiteVillage(Object[] row) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("nikshayVillageID", row[0]);
m.put("villageName", row[1]);
m.put("nikshayFacilityID", row[2]);
return m;
}

private List<Integer> parseIntCsv(String csv) {
return Arrays.stream(csv.split(","))
.map(String::trim)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.admin.controller.stoptbteam;

import javax.ws.rs.core.MediaType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.iemr.admin.service.stoptbteam.StopTBTeamService;
import com.iemr.admin.utils.mapper.OutputMapper;
import com.iemr.admin.utils.response.OutputResponse;

import io.swagger.v3.oas.annotations.Operation;

/** Stop TB teams (MMU vans) for Work Location Mapping (AMM-2496). */
@RestController
@RequestMapping(value = "/stoptb/team")
public class StopTBTeamController {
private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

@Autowired

Check warning on line 46 in src/main/java/com/iemr/admin/controller/stoptbteam/StopTBTeamController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this field injection and use constructor injection instead.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWcY0NW0YKJTPbK2&open=AaGbnWcY0NW0YKJTPbK2&pullRequest=160
private StopTBTeamService teamService;

@Operation(summary = "Stop TB teams (MMU vans) of a state")
@GetMapping(value = "/vans", produces = MediaType.APPLICATION_JSON)
public String getStateTeams(@RequestParam("stateID") Integer stateID,
@RequestParam("serviceProviderID") Integer serviceProviderID) {
OutputResponse response = new OutputResponse();
try {
response.setResponse(OutputMapper.gsonWithoutExpose().toJson(teamService.stateTeams(stateID, serviceProviderID)));
} catch (Exception e) {
logger.error("Stop TB teams for state {} failed", stateID, e);
response.setError(e);
}
return response.toString();
}

@Operation(summary = "A Stop TB user's current team")
@GetMapping(value = "/user", produces = MediaType.APPLICATION_JSON)
public String getUserTeam(@RequestParam("userID") Integer userID,
@RequestParam("providerServiceMapID") Integer providerServiceMapID) {
OutputResponse response = new OutputResponse();
try {
response.setResponse(OutputMapper.gsonWithoutExpose().toJson(teamService.userTeam(userID, providerServiceMapID)));
} catch (Exception e) {
logger.error("Stop TB team of user {} failed", userID, e);
response.setError(e);
}
return response.toString();
}

@Operation(summary = "Members of a Stop TB team")
@GetMapping(value = "/members", produces = MediaType.APPLICATION_JSON)
public String getTeamMembers(@RequestParam("vanID") Integer vanID,
@RequestParam("providerServiceMapID") Integer providerServiceMapID) {
OutputResponse response = new OutputResponse();
try {
response.setResponse(OutputMapper.gsonWithoutExpose().toJson(teamService.teamMembers(vanID, providerServiceMapID)));
} catch (Exception e) {
logger.error("Stop TB team {} members failed", vanID, e);
response.setError(e);
}
return response.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.admin.controller.villagemapping;

import javax.ws.rs.core.MediaType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.iemr.admin.model.villagemapping.VillageUpdateRequest;
import com.iemr.admin.service.villagemapping.VillageUpdateService;
import com.iemr.admin.utils.mapper.OutputMapper;
import com.iemr.admin.utils.response.OutputResponse;

import io.swagger.v3.oas.annotations.Operation;

@RestController
@RequestMapping(value = "/villageMapping")
public class VillageUpdateController {
private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

@Autowired

Check warning on line 46 in src/main/java/com/iemr/admin/controller/villagemapping/VillageUpdateController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this field injection and use constructor injection instead.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWcX0NW0YKJTPbK0&open=AaGbnWcX0NW0YKJTPbK0&pullRequest=160
private VillageUpdateService villageUpdateService;

@Operation(summary = "Replace a village on a user's work location mapping and move their beneficiary addresses")
@RequestMapping(value = "/updateVillage", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON, headers = "Authorization")

Check warning on line 50 in src/main/java/com/iemr/admin/controller/villagemapping/VillageUpdateController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "@RequestMapping(method = RequestMethod.POST)" with "@PostMapping"

See more on https://sonarcloud.io/project/issues?id=PSMRI_Admin-API&issues=AaGbnWcX0NW0YKJTPbK1&open=AaGbnWcX0NW0YKJTPbK1&pullRequest=160
public String updateVillage(@RequestBody VillageUpdateRequest updateRequest) {
OutputResponse response = new OutputResponse();
try {
logger.info("updateVillage received request");
response.setResponse(OutputMapper.gsonWithoutExpose().toJson(villageUpdateService.updateVillage(updateRequest)));
} catch (Exception e) {
logger.error("updateVillage failed", e);
response.setError(e);
}
logger.info("updateVillage sending response");
return response.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,12 @@ public class M_UserServiceRoleMapping2 {
@Column(name = "NikshayFacilityID")
private String nikshayFacilityID;

// Stop TB team (AMM-2496): the van (camp laptop) the user works with. Same
// value on all of the user's active Stop TB rows; null when not in a team.
@Expose
@Column(name = "VanID")
private Integer vanID;

public M_UserServiceRoleMapping2() {
}

Expand Down
11 changes: 10 additions & 1 deletion src/main/java/com/iemr/admin/data/items/ItemMaster.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,16 @@ public class ItemMaster {
@Expose
@Column(name="Strength")
private String strength;


// Stock threshold: at or below this quantity a store's Low Stock report lists the item
@Expose
@Column(name="Min_Stock")
private Integer minStock;

@Expose
@Column(name="Max_Stock")
private Integer maxStock;

@Expose
@Column(name="UOMID")
private Integer uomID;
Expand Down
Loading
Loading