From da4ab3166b1eb8f3dd2fbf22607c12bb0f8d6470 Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Fri, 25 Sep 2026 13:20:35 +0530 Subject: [PATCH 1/3] fix: update the report with strength and UOM (#135) --- .../model/report/DailyStockDetails.java | 4 + .../inventory/model/report/MonthlyReport.java | 4 + .../inventory/model/report/YearlyReport.java | 4 + .../service/report/CRMReportServiceImpl.java | 336 +++++++----------- 4 files changed, 143 insertions(+), 205 deletions(-) diff --git a/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java b/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java index e1decf82..341a97c7 100644 --- a/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java +++ b/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java @@ -42,6 +42,10 @@ public class DailyStockDetails { private String itemCategory; + private String strength; + + private String uom; + private String batchNo; private Double unitCostPrice; diff --git a/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java b/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java index ca5f632e..8aa1e05f 100644 --- a/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java +++ b/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java @@ -44,6 +44,10 @@ public class MonthlyReport { private String itemCategory; + private String strength; + + private String uom; + private String batchNo; private Double unitCostPrice; diff --git a/src/main/java/com/iemr/inventory/model/report/YearlyReport.java b/src/main/java/com/iemr/inventory/model/report/YearlyReport.java index ce046736..058debe4 100644 --- a/src/main/java/com/iemr/inventory/model/report/YearlyReport.java +++ b/src/main/java/com/iemr/inventory/model/report/YearlyReport.java @@ -41,6 +41,10 @@ public class YearlyReport { private String itemCategory; + private String strength; + + private String uom; + private String batchNo; private Double unitCostPrice; diff --git a/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java b/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java index d27e7048..5bd5d809 100644 --- a/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java +++ b/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java @@ -23,6 +23,8 @@ import java.sql.Date; import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -61,6 +63,88 @@ public class CRMReportServiceImpl implements CRMReportService { private Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName()); + private StockDetailRow mapStockDetailRow(Object[] row) { + if (row == null || row.length < 19) { + throw new IllegalArgumentException("PR_StockDetail returned an unexpected number of columns"); + } + + StockDetailRow detail = new StockDetailRow(); + detail.facilityName = text(row[3]); + detail.itemName = text(row[5]); + detail.strength = text(row[6]); + detail.uom = text(row[7]); + detail.itemCategoryName = text(row[8]); + detail.batchNo = text(row[9]); + detail.unitCostPrice = decimal(row[10]); + detail.expiryDate = sqlDate(row[11]); + detail.quantityReceived = wholeNumber(row[12]); + detail.openingStock = wholeNumber(row[13]); + detail.dispensedQuantity = wholeNumber(row[14]); + detail.adjustmentReceipt = wholeNumber(row[15]); + detail.adjustmentIssue = wholeNumber(row[16]); + detail.closingStock = wholeNumber(row[17]); + detail.itemEnteredDate = sqlTimestamp(row[18]); + return detail; + } + + private String text(Object value) { + return value == null ? null : value.toString(); + } + + private Long wholeNumber(Object value) { + return value == null ? 0L : Long.valueOf(value.toString()); + } + + private Double decimal(Object value) { + return value == null ? 0.0 : Double.valueOf(value.toString()); + } + + private Date sqlDate(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date) { + return (Date) value; + } + try { + return new Date(new SimpleDateFormat("dd-MM-yyyy").parse(value.toString()).getTime()); + } catch (ParseException e) { + throw new IllegalArgumentException("Unable to parse PR_StockDetail date: " + value, e); + } + } + + private Timestamp sqlTimestamp(Object value) { + if (value == null) { + return null; + } + if (value instanceof Timestamp) { + return (Timestamp) value; + } + try { + return new Timestamp(new SimpleDateFormat("dd-MM-yyyy").parse(value.toString()).getTime()); + } catch (ParseException e) { + throw new IllegalArgumentException("Unable to parse PR_StockDetail timestamp: " + value, e); + } + } + + private static class StockDetailRow { + private String facilityName; + private String itemName; + private String strength; + private String uom; + private String itemCategoryName; + private String batchNo; + private Double unitCostPrice; + private Date expiryDate; + private Long quantityReceived; + private Long openingStock; + private Long dispensedQuantity; + private Long adjustmentReceipt; + private Long adjustmentIssue; + private Long closingStock; + private Timestamp itemEnteredDate; + } + @Override public String getInwardStockReport(ItemStockEntryReport entryReport) { @@ -213,85 +297,25 @@ public String getDailyStockDetailsReport(ItemStockEntryReport entryReport) { for (Object[] objects : reports) { if (objects != null && objects.length > 0) { - String batchNo = objects[3] != null ? objects[3].toString() : null; - Long totalQuantityReceived = 0L; - if (objects[4] != null) { - totalQuantityReceived = Long.valueOf(objects[4].toString()); - } - Double unitCostPrice = 0.0; - if (objects[5] != null) { - unitCostPrice = Double.valueOf(objects[5].toString()); - } - Date expiryDate = (Date) objects[6]; - Long openingStock = 0L; - if (objects[10] != null) { - openingStock = Long.valueOf(objects[10].toString()); - } - Long adjustedQuantity_FromDate = 0L; - if (objects[15] != null) { - adjustedQuantity_FromDate = Long.valueOf(objects[15].toString()); - } - Long quantityDispanced = 0L; - if (objects[11] != null) { - quantityDispanced = Long.valueOf(objects[11].toString()); - } - String itemName = objects[12] != null ? objects[12].toString() : null; - String facilityName = objects[13] != null ? objects[13].toString() : null; - String itemCategoryName = objects[14] != null ? objects[14].toString() : null; - Long adjustedQuantity_ToDate = 0L; - if (objects[15] != null) { - adjustedQuantity_ToDate = Long.valueOf(objects[15].toString()); - } - Long adjustedQuantity_ToDate_Receipt = 0L; - if (objects[16] != null) { - adjustedQuantity_ToDate_Receipt = Long.valueOf(objects[16].toString()); - } - Long adjustedQuantity_ToDate_Issue = 0L; - if (objects[17] != null) { - adjustedQuantity_ToDate_Issue = Long.valueOf(objects[17].toString()); - } - Long ClosingStock = 0L; - if (objects[18] != null) { - ClosingStock = Long.valueOf(objects[18].toString()); - } - - -// Long actualOpening = openingStock + adjustedQuantity_FromDate; - Long actualOpening = openingStock; - Long actualDispensed = quantityDispanced;// - adjustedQuantity_ToDate; - Long actualClosing = ClosingStock; -// if (actualOpening == 0 || actualOpening == null) { -// actualClosing = totalQuantityReceived - actualDispensed + adjustedQuantity_ToDate; -// } -// -// else { -// actualClosing = actualOpening - actualDispensed + adjustedQuantity_ToDate; -// totalQuantityReceived = 0L; -// } - - Timestamp itemEnteredDate = null; - if(objects[9]!=null) { - itemEnteredDate = (Timestamp) objects[9]; - - } + StockDetailRow detail = mapStockDetailRow(objects); DailyStockDetails stockDetail = new DailyStockDetails(); stockDetail.setSlNo(slNo++); stockDetail.setDate(entryReport.getStartDate()); - stockDetail.setFacilityName(facilityName); - stockDetail.setItemName(itemName); - stockDetail.setItemCategory(itemCategoryName); - stockDetail.setBatchNo(batchNo); - stockDetail.setUnitCostPrice(unitCostPrice); - stockDetail.setExpiryDate(expiryDate); - stockDetail.setOpeningStock(actualOpening); - stockDetail.setQuantityReceived(totalQuantityReceived); - stockDetail.setDispensedQuantity(actualDispensed); - stockDetail.setClosingStock(actualClosing); - - stockDetail.setItemEnteredDate(itemEnteredDate); - - stockDetail.setAdjustmentIssue(adjustedQuantity_ToDate_Issue); - stockDetail.setAdjustmentReceipt(adjustedQuantity_ToDate_Receipt); + stockDetail.setFacilityName(detail.facilityName); + stockDetail.setItemName(detail.itemName); + stockDetail.setItemCategory(detail.itemCategoryName); + stockDetail.setStrength(detail.strength); + stockDetail.setUom(detail.uom); + stockDetail.setBatchNo(detail.batchNo); + stockDetail.setUnitCostPrice(detail.unitCostPrice); + stockDetail.setExpiryDate(detail.expiryDate); + stockDetail.setOpeningStock(detail.openingStock); + stockDetail.setQuantityReceived(detail.quantityReceived); + stockDetail.setDispensedQuantity(detail.dispensedQuantity); + stockDetail.setClosingStock(detail.closingStock); + stockDetail.setItemEnteredDate(detail.itemEnteredDate); + stockDetail.setAdjustmentIssue(detail.adjustmentIssue); + stockDetail.setAdjustmentReceipt(detail.adjustmentReceipt); list.add(stockDetail); } } @@ -411,83 +435,26 @@ public String getMonthlyReport(ItemStockEntryReport entryReport) { for (Object[] objects : reports) { if (objects != null && objects.length > 0) { - String batchNo = objects[3] != null ? objects[3].toString() : null; - Long totalQuantityReceived = 0L; - if (objects[4] != null) { - totalQuantityReceived = Long.valueOf(objects[4].toString()); - } - Double unitCostPrice = 0.0; - if (objects[5] != null) { - unitCostPrice = Double.valueOf(objects[5].toString()); - } - Date expiryDate = (Date) objects[6]; - Long openingStock = 0L; - if (objects[10] != null) { - openingStock = Long.valueOf(objects[10].toString()); - } - Long adjustedQuantity_FromDate = 0L; - if (objects[15] != null) { - adjustedQuantity_FromDate = Long.valueOf(objects[15].toString()); - } - Long quantityDispanced = 0L; - if (objects[11] != null) { - quantityDispanced = Long.valueOf(objects[11].toString()); - } - String itemName = objects[12] != null ? objects[12].toString() : null; - String facilityName = objects[13] != null ? objects[13].toString() : null; - String itemCategoryName = objects[14] != null ? objects[14].toString() : null; - Long adjustedQuantity_ToDate = 0L; - if (objects[15] != null) { - adjustedQuantity_ToDate = Long.valueOf(objects[15].toString()); - } - Long adjustedQuantity_ToDate_Receipt = 0L; - if (objects[16] != null) { - adjustedQuantity_ToDate_Receipt = Long.valueOf(objects[16].toString()); - } - Long adjustedQuantity_ToDate_Issue = 0L; - if (objects[17] != null) { - adjustedQuantity_ToDate_Issue = Long.valueOf(objects[17].toString()); - } - Long ClosingStock = 0L; - if (objects[18] != null) { - ClosingStock = Long.valueOf(objects[18].toString()); - } -// Long actualOpening = openingStock + adjustedQuantity_FromDate; - Long actualOpening = openingStock; - Long actualDispensed = quantityDispanced;// - adjustedQuantity_ToDate; - Long actualClosing = ClosingStock; -// Long actualClosing = 0L; -// if (actualOpening == 0 || actualOpening == null) { -// actualClosing = totalQuantityReceived - actualDispensed + adjustedQuantity_ToDate; -// } else { -// actualClosing = actualOpening - actualDispensed + adjustedQuantity_ToDate; -// totalQuantityReceived = 0L; -// } - - Timestamp itemEnteredDate = null; - if(objects[9]!=null) { - itemEnteredDate = (Timestamp) objects[9]; - - } + StockDetailRow detail = mapStockDetailRow(objects); MonthlyReport stockDetail = new MonthlyReport(); stockDetail.setSlNo(slNo++); stockDetail.setMonth(entryReport.getMonthName()); stockDetail.setYear(entryReport.getYear()); - stockDetail.setFacilityName(facilityName); - stockDetail.setItemName(itemName); - stockDetail.setItemCategory(itemCategoryName); - stockDetail.setBatchNo(batchNo); - stockDetail.setUnitCostPrice(unitCostPrice); - stockDetail.setExpiryDate(expiryDate); - stockDetail.setOpeningStock(actualOpening); - stockDetail.setQuantityReceived(totalQuantityReceived); - stockDetail.setDispensedQuantity(actualDispensed); - stockDetail.setClosingStock(actualClosing); - - stockDetail.setItemEnteredDate(itemEnteredDate); - - stockDetail.setAdjustmentIssue(adjustedQuantity_ToDate_Issue); - stockDetail.setAdjustmentReceipt(adjustedQuantity_ToDate_Receipt); + stockDetail.setFacilityName(detail.facilityName); + stockDetail.setItemName(detail.itemName); + stockDetail.setItemCategory(detail.itemCategoryName); + stockDetail.setStrength(detail.strength); + stockDetail.setUom(detail.uom); + stockDetail.setBatchNo(detail.batchNo); + stockDetail.setUnitCostPrice(detail.unitCostPrice); + stockDetail.setExpiryDate(detail.expiryDate); + stockDetail.setOpeningStock(detail.openingStock); + stockDetail.setQuantityReceived(detail.quantityReceived); + stockDetail.setDispensedQuantity(detail.dispensedQuantity); + stockDetail.setClosingStock(detail.closingStock); + stockDetail.setItemEnteredDate(detail.itemEnteredDate); + stockDetail.setAdjustmentIssue(detail.adjustmentIssue); + stockDetail.setAdjustmentReceipt(detail.adjustmentReceipt); list.add(stockDetail); } } @@ -525,66 +492,25 @@ public String getYearlyReport(ItemStockEntryReport entryReport) { for (Object[] objects : reports) { if (objects != null && objects.length > 0) { - String batchNo = objects[3] != null ? objects[3].toString() : null; - Long totalQuantityReceived = 0L; - if (objects[4] != null) { - totalQuantityReceived = Long.valueOf(objects[4].toString()); - } - Double unitCostPrice = 0.0; - if (objects[5] != null) { - unitCostPrice = Double.valueOf(objects[5].toString()); - } - Date expiryDate = (Date) objects[6]; - Long openingStock = 0L; - if (objects[10] != null) { - openingStock = Long.valueOf(objects[10].toString()); - } - Long adjustedQuantity_FromDate = 0L; - if (objects[15] != null) { - adjustedQuantity_FromDate = Long.valueOf(objects[15].toString()); - } - Long quantityDispanced = 0L; - if (objects[11] != null) { - quantityDispanced = Long.valueOf(objects[11].toString()); - } - String itemName = objects[12] != null ? objects[12].toString() : null; - String facilityName = objects[13] != null ? objects[13].toString() : null; - String itemCategoryName = objects[14] != null ? objects[14].toString() : null; - Long adjustedQuantity_ToDate = 0L; - if (objects[15] != null) { - adjustedQuantity_ToDate = Long.valueOf(objects[15].toString()); - } - Long adjustedQuantity_ToDate_Receipt = 0L; - if (objects[16] != null) { - adjustedQuantity_ToDate_Receipt = Long.valueOf(objects[16].toString()); - } - Long adjustedQuantity_ToDate_Issue = 0L; - if (objects[17] != null) { - adjustedQuantity_ToDate_Issue = Long.valueOf(objects[17].toString()); - } - Long ClosingStock = 0L; - if (objects[18] != null) { - ClosingStock = Long.valueOf(objects[18].toString()); - } - Long actualOpening = openingStock; - Long actualDispensed = quantityDispanced; - Long actualClosing = ClosingStock; + StockDetailRow detail = mapStockDetailRow(objects); YearlyReport stockDetail = new YearlyReport(); stockDetail.setSlNo(slNo++); stockDetail.setYear(entryReport.getYear()); - stockDetail.setFacilityName(facilityName); - stockDetail.setItemName(itemName); - stockDetail.setItemCategory(itemCategoryName); - stockDetail.setBatchNo(batchNo); - stockDetail.setUnitCostPrice(unitCostPrice); - stockDetail.setExpiryDate(expiryDate); - stockDetail.setOpeningStock(actualOpening); - stockDetail.setQuantityReceived(totalQuantityReceived); - stockDetail.setDispensedQuantity(actualDispensed); - stockDetail.setClosingStock(actualClosing); - stockDetail.setAdjustmentIssue(adjustedQuantity_ToDate_Issue); - stockDetail.setAdjustmentReceipt(adjustedQuantity_ToDate_Receipt); + stockDetail.setFacilityName(detail.facilityName); + stockDetail.setItemName(detail.itemName); + stockDetail.setItemCategory(detail.itemCategoryName); + stockDetail.setStrength(detail.strength); + stockDetail.setUom(detail.uom); + stockDetail.setBatchNo(detail.batchNo); + stockDetail.setUnitCostPrice(detail.unitCostPrice); + stockDetail.setExpiryDate(detail.expiryDate); + stockDetail.setOpeningStock(detail.openingStock); + stockDetail.setQuantityReceived(detail.quantityReceived); + stockDetail.setDispensedQuantity(detail.dispensedQuantity); + stockDetail.setClosingStock(detail.closingStock); + stockDetail.setAdjustmentIssue(detail.adjustmentIssue); + stockDetail.setAdjustmentReceipt(detail.adjustmentReceipt); list.add(stockDetail); } } From 3cb5480a45f412fd414e5fd114f750b7c7e88c67 Mon Sep 17 00:00:00 2001 From: SnehaRH <77656297+snehar-nd@users.noreply.github.com> Date: Fri, 25 Sep 2026 19:11:09 +0530 Subject: [PATCH 2/3] fix: add EntryType and item form to stock detail reports (#137) PR_StockDetail returns EntryType and item form, but the daily stock details, monthly and yearly reports dropped them. Map both into all three reports, and add Item Entered Date to the yearly report. Co-authored-by: Claude Opus 5.5 (1M context) --- .../inventory/model/report/DailyStockDetails.java | 4 ++++ .../iemr/inventory/model/report/MonthlyReport.java | 4 ++++ .../com/iemr/inventory/model/report/YearlyReport.java | 7 +++++++ .../service/report/CRMReportServiceImpl.java | 11 +++++++++++ 4 files changed, 26 insertions(+) diff --git a/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java b/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java index 341a97c7..ffd143d7 100644 --- a/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java +++ b/src/main/java/com/iemr/inventory/model/report/DailyStockDetails.java @@ -36,8 +36,12 @@ public class DailyStockDetails { private Timestamp date; + private String entryType; + private String facilityName; + private String itemForm; + private String itemName; private String itemCategory; diff --git a/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java b/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java index 8aa1e05f..d784aed5 100644 --- a/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java +++ b/src/main/java/com/iemr/inventory/model/report/MonthlyReport.java @@ -38,8 +38,12 @@ public class MonthlyReport { private Integer year; + private String entryType; + private String facilityName; + private String itemForm; + private String itemName; private String itemCategory; diff --git a/src/main/java/com/iemr/inventory/model/report/YearlyReport.java b/src/main/java/com/iemr/inventory/model/report/YearlyReport.java index 058debe4..c9599381 100644 --- a/src/main/java/com/iemr/inventory/model/report/YearlyReport.java +++ b/src/main/java/com/iemr/inventory/model/report/YearlyReport.java @@ -22,6 +22,7 @@ package com.iemr.inventory.model.report; import java.sql.Date; +import java.sql.Timestamp; import com.google.gson.GsonBuilder; import com.google.gson.LongSerializationPolicy; @@ -35,8 +36,12 @@ public class YearlyReport { private Integer year; + private String entryType; + private String facilityName; + private String itemForm; + private String itemName; private String itemCategory; @@ -63,6 +68,8 @@ public class YearlyReport { private Long closingStock; + private Timestamp itemEnteredDate; + @Override public String toString() { diff --git a/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java b/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java index 5bd5d809..b41a6403 100644 --- a/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java +++ b/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java @@ -69,7 +69,9 @@ private StockDetailRow mapStockDetailRow(Object[] row) { } StockDetailRow detail = new StockDetailRow(); + detail.entryType = text(row[2]); detail.facilityName = text(row[3]); + detail.itemForm = text(row[4]); detail.itemName = text(row[5]); detail.strength = text(row[6]); detail.uom = text(row[7]); @@ -128,7 +130,9 @@ private Timestamp sqlTimestamp(Object value) { } private static class StockDetailRow { + private String entryType; private String facilityName; + private String itemForm; private String itemName; private String strength; private String uom; @@ -301,7 +305,9 @@ public String getDailyStockDetailsReport(ItemStockEntryReport entryReport) { DailyStockDetails stockDetail = new DailyStockDetails(); stockDetail.setSlNo(slNo++); stockDetail.setDate(entryReport.getStartDate()); + stockDetail.setEntryType(detail.entryType); stockDetail.setFacilityName(detail.facilityName); + stockDetail.setItemForm(detail.itemForm); stockDetail.setItemName(detail.itemName); stockDetail.setItemCategory(detail.itemCategoryName); stockDetail.setStrength(detail.strength); @@ -440,7 +446,9 @@ public String getMonthlyReport(ItemStockEntryReport entryReport) { stockDetail.setSlNo(slNo++); stockDetail.setMonth(entryReport.getMonthName()); stockDetail.setYear(entryReport.getYear()); + stockDetail.setEntryType(detail.entryType); stockDetail.setFacilityName(detail.facilityName); + stockDetail.setItemForm(detail.itemForm); stockDetail.setItemName(detail.itemName); stockDetail.setItemCategory(detail.itemCategoryName); stockDetail.setStrength(detail.strength); @@ -497,7 +505,9 @@ public String getYearlyReport(ItemStockEntryReport entryReport) { YearlyReport stockDetail = new YearlyReport(); stockDetail.setSlNo(slNo++); stockDetail.setYear(entryReport.getYear()); + stockDetail.setEntryType(detail.entryType); stockDetail.setFacilityName(detail.facilityName); + stockDetail.setItemForm(detail.itemForm); stockDetail.setItemName(detail.itemName); stockDetail.setItemCategory(detail.itemCategoryName); stockDetail.setStrength(detail.strength); @@ -509,6 +519,7 @@ public String getYearlyReport(ItemStockEntryReport entryReport) { stockDetail.setQuantityReceived(detail.quantityReceived); stockDetail.setDispensedQuantity(detail.dispensedQuantity); stockDetail.setClosingStock(detail.closingStock); + stockDetail.setItemEnteredDate(detail.itemEnteredDate); stockDetail.setAdjustmentIssue(detail.adjustmentIssue); stockDetail.setAdjustmentReceipt(detail.adjustmentReceipt); list.add(stockDetail); From 146277a7fe4b4b7a5de720049e8b8bf94b797fe4 Mon Sep 17 00:00:00 2001 From: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:31:15 +0530 Subject: [PATCH 3/3] feat(AMM-2491): low / out-of-stock report and re-upload of changed camp stock batches (#138) * feat(AMM-2491): low stock and out of stock report for a store POST /crmReportController/getLowStockReport {facilityID}: items mapped to the store whose usable (not expired) stock is 0, or at/below the item's Min_Stock. Reads live stock from db_iemr, not the reporting copy. Co-Authored-By: Claude Opus 5.5 * feat(AMM-2491): StopTB laptop uploads a batch again when its stock changes On StopTB camp laptops (stoptb.enforce.vanid=true) a batch whose QuantityInHand changes (stock adjustment, stock exit/dispense, patient return, indent issue) is marked Processed='N', so the next upload sends its current stock and central's camp-stock reports match the camp. Other deployments unchanged. Co-Authored-By: Claude Opus 5.5 * chore: set version to 3.12.0 Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- pom.xml | 2 +- .../report/CRMReportController.java | 19 +++++ .../model/report/LowStockReport.java | 76 +++++++++++++++++++ .../repo/report/ItemStockReportRepo.java | 15 ++++ .../repo/stockEntry/ItemStockEntryRepo.java | 10 +++ .../service/indent/IndentServiceImpl.java | 5 ++ .../PatientReturnServiceImpl.java | 5 ++ .../service/report/CRMReportService.java | 2 + .../service/report/CRMReportServiceImpl.java | 24 ++++++ .../stockEntry/StockEntryServiceImpl.java | 5 ++ .../service/stockEntry/StockUploadMarker.java | 53 +++++++++++++ .../StockAdjustmentServiceImpl.java | 5 ++ 12 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/iemr/inventory/model/report/LowStockReport.java create mode 100644 src/main/java/com/iemr/inventory/service/stockEntry/StockUploadMarker.java diff --git a/pom.xml b/pom.xml index 5b2ebce2..a7f2a440 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.iemr.inventory inventory-api - 3.9.0 + 3.12.0 war Inventory-API Inventory Page diff --git a/src/main/java/com/iemr/inventory/controller/report/CRMReportController.java b/src/main/java/com/iemr/inventory/controller/report/CRMReportController.java index 3627768d..3ed55557 100644 --- a/src/main/java/com/iemr/inventory/controller/report/CRMReportController.java +++ b/src/main/java/com/iemr/inventory/controller/report/CRMReportController.java @@ -86,6 +86,25 @@ public String getExpiryReport(@RequestBody String request) { return response.toString(); } + @ApiOperation(value = "Get low stock and out of stock report for a store", consumes = "application/json", produces = "application/json") + @RequestMapping(value = "/getLowStockReport", headers = "Authorization", method = { + RequestMethod.POST }, produces = { "application/json" }) + public String getLowStockReport(@RequestBody String request) { + logger.info("getLowStockReport request " + request.toString()); + OutputResponse response = new OutputResponse(); + try { + + ItemStockEntryReport report = InputMapper.gson().fromJson(request, ItemStockEntryReport.class); + String res = crmReportService.getLowStockReport(report); + response.setResponse(res); + logger.info("getLowStockReport response " + response.toString()); + } catch (Exception e) { + logger.error(e.getMessage()); + response.setError(e); + } + return response.toString(); + } + @ApiOperation(value = "Get consumption report", consumes = "application/json", produces = "application/json") @RequestMapping(value = "/getConsumptionReport", headers = "Authorization", method = { RequestMethod.POST }, produces = { "application/json" }) diff --git a/src/main/java/com/iemr/inventory/model/report/LowStockReport.java b/src/main/java/com/iemr/inventory/model/report/LowStockReport.java new file mode 100644 index 00000000..7b8e95b5 --- /dev/null +++ b/src/main/java/com/iemr/inventory/model/report/LowStockReport.java @@ -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.inventory.model.report; + +import com.google.gson.GsonBuilder; +import com.google.gson.LongSerializationPolicy; + +import lombok.Data; + +@Data +public class LowStockReport { + + private Long slNo; + + private String facilityName; + + private Integer itemID; + + private String itemName; + + private String itemCategory; + + private String strength; + + private Integer minStock; + + private Integer quantityInHand; + + // "Out of stock" (nothing usable left) or "Low stock" (at or below the item's minimum stock) + private String stockStatus; + + public LowStockReport() { + + } + + public LowStockReport(String facilityName, Integer itemID, String itemName, String itemCategory, String strength, + Integer minStock, Integer quantityInHand) { + + this.facilityName = facilityName; + this.itemID = itemID; + this.itemName = itemName; + this.itemCategory = itemCategory; + this.strength = strength; + this.minStock = minStock; + this.quantityInHand = quantityInHand; + this.stockStatus = (quantityInHand == null || quantityInHand <= 0) ? "Out of stock" : "Low stock"; + } + + @Override + public String toString() { + + return new GsonBuilder().setLongSerializationPolicy(LongSerializationPolicy.STRING).serializeNulls().create() + .toJson(this); + + } + +} diff --git a/src/main/java/com/iemr/inventory/repo/report/ItemStockReportRepo.java b/src/main/java/com/iemr/inventory/repo/report/ItemStockReportRepo.java index 99a9ca67..efcdaef8 100644 --- a/src/main/java/com/iemr/inventory/repo/report/ItemStockReportRepo.java +++ b/src/main/java/com/iemr/inventory/repo/report/ItemStockReportRepo.java @@ -57,6 +57,21 @@ List getItemStockEntryReportByFacilityID(@Param("startDate List getExpiryReportByFacilityID(@Param("startDate") Date startDate, @Param("endDate") Date endDate, @Param("facilityID") Integer facilityID); + // Live stock (db_iemr, not the reporting copy): items mapped to the store whose usable (not expired) stock + // is 0 or at/below the item's Min_Stock. + @Query(value = "SELECT f.FacilityName, i.ItemID, i.ItemName, c.ItemCategoryName, i.Strength, i.Min_Stock, " + + "COALESCE((SELECT SUM(e.QuantityInHand) FROM db_iemr.t_itemstockentry e WHERE e.FacilityID = f.FacilityID " + + " AND e.ItemID = i.ItemID AND e.Deleted = false AND (e.ExpiryDate IS NULL OR e.ExpiryDate >= CURDATE())), 0) AS inHand " + + "FROM db_iemr.m_facility f " + + "JOIN db_iemr.m_item i ON i.ItemID IN (SELECT m.ItemID FROM db_iemr.m_itemfacilitymapping m " + + " WHERE m.FacilityID = f.FacilityID AND m.Deleted = false) AND i.Deleted = false " + + " AND (i.Discontinued IS NULL OR i.Discontinued = false) " + + "LEFT JOIN db_iemr.m_itemcategory c ON c.ItemCategoryID = i.ItemCategoryID " + + "WHERE f.FacilityID = :facilityID " + + "HAVING inHand <= 0 OR (i.Min_Stock > 0 AND inHand <= i.Min_Stock) " + + "ORDER BY inHand ASC, i.ItemName ASC", nativeQuery = true) + List getLowStockReportByFacilityID(@Param("facilityID") Integer facilityID); + @Query(value="Select ISEx.*, db.BeneficiaryID, concat(IFNULL(db.Title,''),' ', IFNULL(db.FirstName,''),' ', " + "IFNULL(db.MiddleName,''),' ', IFNULL(db.LastName,'')) Name from db_reporting.fact_itemstockexit ISEx left join db_iemr.t_patientissue PI " + "on ISEx.ExitTypeID = PI.VanSerialNo and ISEx.VanID = PI.VanID " diff --git a/src/main/java/com/iemr/inventory/repo/stockEntry/ItemStockEntryRepo.java b/src/main/java/com/iemr/inventory/repo/stockEntry/ItemStockEntryRepo.java index 915c2cb7..95495a80 100644 --- a/src/main/java/com/iemr/inventory/repo/stockEntry/ItemStockEntryRepo.java +++ b/src/main/java/com/iemr/inventory/repo/stockEntry/ItemStockEntryRepo.java @@ -94,6 +94,16 @@ List findByItemIDInQuantityInHandGreaterThanForFacilityID(@Param("item @Query("UPDATE ItemStockEntry c SET c.quantityInHand = c.quantityInHand + :quant WHERE c.itemStockEntryID = :id") Integer addStock(@Param("id") Long id, @Param("quant") Integer quant); + @Transactional + @Modifying + @Query("UPDATE ItemStockEntry c SET c.processed = 'N' WHERE c.itemStockEntryID = :id") + Integer markForUpload(@Param("id") Long id); + + @Transactional + @Modifying + @Query("UPDATE ItemStockEntry c SET c.processed = 'N' WHERE c.vanSerialNo = :vanSerialNo and c.facilityID = :facilityID") + Integer markForUploadByVanSerialNo(@Param("vanSerialNo") Long vanSerialNo, @Param("facilityID") Integer facilityID); + @Transactional @Modifying @Query("UPDATE ItemStockEntry c SET c.quantityInHand = c.quantityInHand - :quant WHERE c.itemStockEntryID = :id") diff --git a/src/main/java/com/iemr/inventory/service/indent/IndentServiceImpl.java b/src/main/java/com/iemr/inventory/service/indent/IndentServiceImpl.java index e7dd4d3f..e354ad16 100644 --- a/src/main/java/com/iemr/inventory/service/indent/IndentServiceImpl.java +++ b/src/main/java/com/iemr/inventory/service/indent/IndentServiceImpl.java @@ -40,6 +40,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import com.iemr.inventory.service.stockEntry.StockUploadMarker; import org.springframework.stereotype.Service; import com.iemr.inventory.data.indent.Indent; @@ -59,6 +60,9 @@ @Service public class IndentServiceImpl implements IndentService{ + @Autowired + private StockUploadMarker stockUploadMarker; + private static final Logger logger = LoggerFactory.getLogger(IndentServiceImpl.class); @PersistenceContext @@ -237,6 +241,7 @@ public String issueIndent(IndentIssue[] array) { { indentOrderRepo.updateQuantityInStock(indentIssue.getIssuedQty(), indentIssue.getItemStockEntryID(), indentIssue.getFromFacilityID()); + stockUploadMarker.batchChanged(indentIssue.getItemStockEntryID()); indentIssue.setCreatedDate(new Timestamp(System.currentTimeMillis())); indentIssue.setIssueDate(new Timestamp(System.currentTimeMillis())); diff --git a/src/main/java/com/iemr/inventory/service/patientreturn/PatientReturnServiceImpl.java b/src/main/java/com/iemr/inventory/service/patientreturn/PatientReturnServiceImpl.java index e5fcc014..276cac3c 100644 --- a/src/main/java/com/iemr/inventory/service/patientreturn/PatientReturnServiceImpl.java +++ b/src/main/java/com/iemr/inventory/service/patientreturn/PatientReturnServiceImpl.java @@ -31,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import com.iemr.inventory.service.stockEntry.StockUploadMarker; import org.springframework.stereotype.Service; import com.iemr.inventory.data.patientreturn.ItemDetailModel; @@ -44,6 +45,9 @@ @Service public class PatientReturnServiceImpl implements PatientReturnService{ + @Autowired + private StockUploadMarker stockUploadMarker; + private static final Logger logger = LoggerFactory.getLogger(PatientReturnServiceImpl.class); @Autowired(required=false) @@ -113,6 +117,7 @@ public String updateQuantityReturned(ItemDetailModel[] itemDetailModel) for(ItemDetailModel itemDetail : list) { int result = patientReturnRepo.updateQuantityReturned(itemDetail.getReturnQuantity(), itemDetail.getItemStockEntryID()); + stockUploadMarker.batchChanged(itemDetail.getItemStockEntryID()); patientReturnRepo.updateIssuedQuantity(itemDetail.getReturnQuantity(), itemDetail.getItemStockExitID()); ItemReturnEntry itemReturnEntry = new ItemReturnEntry(); itemReturnEntry.setCreatedBy(itemDetail.getCreatedBy()); diff --git a/src/main/java/com/iemr/inventory/service/report/CRMReportService.java b/src/main/java/com/iemr/inventory/service/report/CRMReportService.java index 5e6d3dc6..e53977e7 100644 --- a/src/main/java/com/iemr/inventory/service/report/CRMReportService.java +++ b/src/main/java/com/iemr/inventory/service/report/CRMReportService.java @@ -33,6 +33,8 @@ public interface CRMReportService { String getExpiryReport(ItemStockEntryReport entryReport); + String getLowStockReport(ItemStockEntryReport entryReport) throws Exception; + String getConsumptionReport(ItemStockExitReport exitReport); String getBenDrugIssueReport(PatientIssueExitReport exitReport); diff --git a/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java b/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java index b41a6403..91ddd852 100644 --- a/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java +++ b/src/main/java/com/iemr/inventory/service/report/CRMReportServiceImpl.java @@ -45,6 +45,7 @@ import com.iemr.inventory.model.report.DailyStockSummary; import com.iemr.inventory.model.report.ExpiryReport; import com.iemr.inventory.model.report.InwardStockReport; +import com.iemr.inventory.model.report.LowStockReport; import com.iemr.inventory.model.report.MonthlyReport; import com.iemr.inventory.model.report.TransitReport; import com.iemr.inventory.model.report.YearlyReport; @@ -203,6 +204,29 @@ public String getExpiryReport(ItemStockEntryReport entryReport) { return reportList.toString(); } + @Override + public String getLowStockReport(ItemStockEntryReport entryReport) throws Exception { + + if (entryReport == null || entryReport.getFacilityID() == null) { + throw new Exception("facilityID is required"); + } + List list = itemStockReportRepo.getLowStockReportByFacilityID(entryReport.getFacilityID()); + List reportList = new ArrayList(); + Long slNo = 1L; + for (Object[] object : list) { + if (object != null) { + LowStockReport report = new LowStockReport(text(object[0]), + object[1] != null ? ((Number) object[1]).intValue() : null, text(object[2]), text(object[3]), + text(object[4]), object[5] != null ? ((Number) object[5]).intValue() : null, + object[6] != null ? ((Number) object[6]).intValue() : 0); + report.setSlNo(slNo++); + reportList.add(report); + } + } + + return reportList.toString(); + } + @Override public String getConsumptionReport(ItemStockExitReport exitReport) { diff --git a/src/main/java/com/iemr/inventory/service/stockEntry/StockEntryServiceImpl.java b/src/main/java/com/iemr/inventory/service/stockEntry/StockEntryServiceImpl.java index 73602950..c67bf0c3 100644 --- a/src/main/java/com/iemr/inventory/service/stockEntry/StockEntryServiceImpl.java +++ b/src/main/java/com/iemr/inventory/service/stockEntry/StockEntryServiceImpl.java @@ -55,6 +55,9 @@ @Service public class StockEntryServiceImpl implements StockEntryService { + @Autowired + private StockUploadMarker stockUploadMarker; + @Autowired private PhysicalStockEntryRepo physicalStockEntryRepo; @@ -129,6 +132,8 @@ public Integer updateStocks(List itemissueListUpdated) { for (ItemStockExit itemStockExit : itemissueListUpdated) { cnt = cnt + itemStockEntryRepo.updateStock(itemStockExit.getFacilityID(), itemStockExit.getItemStockEntryID(), itemStockExit.getQuantity()); + stockUploadMarker.batchChangedByVanSerialNo(itemStockExit.getItemStockEntryID(), + itemStockExit.getFacilityID()); } return cnt; diff --git a/src/main/java/com/iemr/inventory/service/stockEntry/StockUploadMarker.java b/src/main/java/com/iemr/inventory/service/stockEntry/StockUploadMarker.java new file mode 100644 index 00000000..2d531312 --- /dev/null +++ b/src/main/java/com/iemr/inventory/service/stockEntry/StockUploadMarker.java @@ -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.inventory.service.stockEntry; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.iemr.inventory.repo.stockEntry.ItemStockEntryRepo; + +/** + * StopTB camp laptops (stoptb.enforce.vanid=true): the camp's stock is reported on central from uploaded rows, so a + * batch whose QuantityInHand changes (adjustment, stock exit, patient return, indent issue) is marked + * Processed='N' and uploaded again; central updates its copy by VanSerialNo + store. Other deployments: no change. + */ +@Component +public class StockUploadMarker { + + @Value("${stoptb.enforce.vanid:false}") + private boolean stopTBLaptop; + + @Autowired + private ItemStockEntryRepo itemStockEntryRepo; + + public void batchChanged(Long itemStockEntryID) { + if (stopTBLaptop && itemStockEntryID != null) + itemStockEntryRepo.markForUpload(itemStockEntryID); + } + + public void batchChangedByVanSerialNo(Long vanSerialNo, Integer facilityID) { + if (stopTBLaptop && vanSerialNo != null && facilityID != null) + itemStockEntryRepo.markForUploadByVanSerialNo(vanSerialNo, facilityID); + } +} diff --git a/src/main/java/com/iemr/inventory/service/stockadjustment/StockAdjustmentServiceImpl.java b/src/main/java/com/iemr/inventory/service/stockadjustment/StockAdjustmentServiceImpl.java index fe002032..eb9457a1 100644 --- a/src/main/java/com/iemr/inventory/service/stockadjustment/StockAdjustmentServiceImpl.java +++ b/src/main/java/com/iemr/inventory/service/stockadjustment/StockAdjustmentServiceImpl.java @@ -29,6 +29,7 @@ import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; +import com.iemr.inventory.service.stockEntry.StockUploadMarker; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -51,6 +52,9 @@ @Service public class StockAdjustmentServiceImpl implements StockAdjustmentService { + @Autowired + private StockUploadMarker stockUploadMarker; + @Autowired(required=false) StockAdjustmentDraftRepo stockAdjustmentDraftRepo; @@ -184,6 +188,7 @@ public StockAdjustment savetransaction(StockAdjustment stockAdjustment) throws I } else { itemStockEntryRepo.subtractStock(action.getItemStockEntryID(), action.getAdjustedQuantity()); } + stockUploadMarker.batchChanged(action.getItemStockEntryID()); });