From e104040456564832fc4189330ba1a3ec029f97da Mon Sep 17 00:00:00 2001
From: Lakshmi Kolli <69940873+kollil@users.noreply.github.com>
Date: Tue, 24 Feb 2026 17:51:46 -0800
Subject: [PATCH 1/3] Parentage dam mismatch in Colony alert (#1643)
* Added a new report in the Colony alert:
Parentage Dam mismatch records
* Fixed a few comments and typos
---
.../study/ParentageDamMismatch.query.xml | 9 +++
.../queries/study/ParentageDamMismatch.sql | 61 ++++++++++++++++++
.../ColonyAlertsNotification.java | 64 +++++++++++++++++++
.../notification/ColonyMgmtNotification.java | 4 ++
4 files changed, 138 insertions(+)
create mode 100644 onprc_ehr/resources/queries/study/ParentageDamMismatch.query.xml
create mode 100644 onprc_ehr/resources/queries/study/ParentageDamMismatch.sql
diff --git a/onprc_ehr/resources/queries/study/ParentageDamMismatch.query.xml b/onprc_ehr/resources/queries/study/ParentageDamMismatch.query.xml
new file mode 100644
index 000000000..5a237c10e
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/ParentageDamMismatch.query.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ Mismatched Genetic and Observed Dams
+
+
+
+
\ No newline at end of file
diff --git a/onprc_ehr/resources/queries/study/ParentageDamMismatch.sql b/onprc_ehr/resources/queries/study/ParentageDamMismatch.sql
new file mode 100644
index 000000000..f9e07f467
--- /dev/null
+++ b/onprc_ehr/resources/queries/study/ParentageDamMismatch.sql
@@ -0,0 +1,61 @@
+/*
+ Added by Kolli, Feb 2026
+ Refer tkt# 14114 for details
+ Display 4 columns: Animal Id, Area, Genetic dam, Observed dam
+
+ Get genetic and observed dam mismatch data.
+ Use the following criteria,
+ * 1. One genetic dam per animal
+ * 2. Included Alive + Dead animals
+ * 3. Excludes animals that have a foster dam
+ * 4. Excludes rows where observedDam or geneticDam IS BLANK
+ * 5. Keeps only mismatches between observed and genetic dams
+ * 6. Excludes old parentage entries, enddate IS BLANK
+*/
+
+SELECT
+ d.Id,
+ d.Id.curLocation.area AS Area,
+ coalesce(p2.parent, '') as geneticDam,
+ coalesce(b.dam, '') as observedDam
+FROM study.demographics d
+
+ LEFT JOIN (
+ SELECT
+ p2.Id,
+ MAX(p2.parent) AS parent
+ FROM study.parentage p2
+ WHERE (p2.method = 'Genetic' OR p2.method = 'Provisional Genetic')
+ AND p2.relationship = 'Dam'
+ AND p2.enddate IS NULL
+ GROUP BY p2.Id
+) p2 ON d.Id = p2.Id
+
+ LEFT JOIN (
+ SELECT
+ p3.Id,
+ MAX(p3.parent) AS parent
+ FROM study.parentage p3
+ WHERE p3.relationship = 'Foster Dam'
+ AND p3.enddate IS NULL
+ GROUP BY p3.Id
+) p3 ON d.Id = p3.Id
+
+ LEFT JOIN study.birth b
+ ON b.Id = d.Id
+
+WHERE d.calculated_status.code IN ('Alive', 'Dead') AND d.qcstate = 18
+ /* exclude foster-dam cases (NULL or blank only) */
+ AND COALESCE(RTRIM(LTRIM(CAST(p3.parent AS VARCHAR(50)))), '') = ''
+
+ /* exclude blank observed dam */
+ AND COALESCE(RTRIM(LTRIM(CAST(b.dam AS VARCHAR(50)))), '') <> ''
+
+ /* exclude blank genetic dam */
+ AND COALESCE(RTRIM(LTRIM(CAST(p2.parent AS VARCHAR(50)))), '') <> ''
+
+ /* mismatch observed vs genetic */
+ AND COALESCE(RTRIM(LTRIM(CAST(b.dam AS VARCHAR(50)))), '') <>
+ COALESCE(RTRIM(LTRIM(CAST(p2.parent AS VARCHAR(50)))), '')
+
+
diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java
index 8a5363df3..3b62bd188 100644
--- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java
+++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyAlertsNotification.java
@@ -312,6 +312,70 @@ protected void roomsReportingNegativeCagesAvailable(final Container c, User u, f
}
}
+ /**
+ * Kollil, Jan, 2026 :
+ * Refer tkt# 14114 for more details
+ * Alert title: WARNING: There are [x total] mismatches of observed and genetic dam data requiring review
+ * Format: Table
+ * 4 columns: Animal Id, Area, Genetic dam, Observed dam
+ * Alert criteria:
+ * 1. One genetic dam per animal
+ * 2. Included Alive + Dead animals
+ * 3. Excludes animals that have a foster dam
+ * 4. Excludes rows where observedDam or geneticDam IS BLANK
+ * 5. Keeps only mismatches between observed and genetic dams
+ * 6. Excludes old parentage entries, enddate IS BLANK
+ */
+ protected void mismatchedObservedAndGeneticDam(final Container c, User u, final StringBuilder msg)
+ {
+ if (QueryService.get().getUserSchema(u, c, "study") == null) {
+ msg.append("Warning: The study schema has not been enabled in this folder, so the alert cannot run.
");
+ return;
+ }
+
+ //Dam mismatch query
+ TableInfo ti = QueryService.get().getUserSchema(u, c, "study").getTable("ParentageDamMismatch", ContainerFilter.Type.AllFolders.create(c, u));
+ TableSelector ts = new TableSelector(ti, null, null);
+ long count = ts.getRowCount();
+
+ //Get num of rows
+ if (count > 0) {
+ msg.append(" WARNING: There are " + count + " mismatches of observed and genetic dam data requiring review.");
+ msg.append(" Click here to view them in a grid\n");
+
+ //Display the report in the email
+ Set columns = new HashSet<>();
+ columns.add(FieldKey.fromString("Id"));
+ columns.add(FieldKey.fromString("area"));
+ columns.add(FieldKey.fromString("geneticdam"));
+ columns.add(FieldKey.fromString("observeddam"));
+
+ final Map colMap = QueryService.get().getColumns(ti, columns);
+ TableSelector ts2 = new TableSelector(ti, colMap.values(), null, null);
+
+ msg.append("
\n");
+ msg.append("");
+ msg.append("");
+ msg.append("| Id | Area | Genetic Dam | Observed Dam |
");
+
+ ts2.forEach(object -> {
+ Results rs = new ResultsImpl(object, colMap);
+ String url = getParticipantURL(c, rs.getString("Id"));
+ msg.append("| " + PageFlowUtil.filter(rs.getString("Id")) + " | \n");
+ msg.append("" + PageFlowUtil.filter(rs.getString("area")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("geneticdam")) + " | ");
+ msg.append("" + PageFlowUtil.filter(rs.getString("observeddam")) + " | ");
+ msg.append("
");
+ });
+ }
+ else {
+ msg.append(" There are NO mismatches of observed and genetic dam data. ");
+ }
+ msg.append("
");
+ msg.append("
\n");
+ }
+ //End of Dam mismatch report
+
/**
* Finds all rooms with animals of mixed viral status
* Modified by Kollil, 2/17/2023
diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyMgmtNotification.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyMgmtNotification.java
index c7d35ab92..1ca060cad 100644
--- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyMgmtNotification.java
+++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/ColonyMgmtNotification.java
@@ -74,6 +74,10 @@ public String getMessageBodyHTML(Container c, User u)
doHousingChecks(c, u, msg);
transfersYesterday(c, u, msg);
roomsWithMixedViralStatus(c, u, msg);
+ /*Added by kollil, Jan, 2026
+ Refer to tkt # 14114
+ */
+ mismatchedObservedAndGeneticDam(c, u, msg);
livingAnimalsWithoutWeight(c, u, msg);
hospitalAnimalsWithoutCase(c, u, msg);
From 994dee80c58efc1daa1a2ee8ff4795101e05cafd Mon Sep 17 00:00:00 2001
From: Ohsudev <76500320+Ohsudev@users.noreply.github.com>
Date: Mon, 16 Mar 2026 10:15:47 -0700
Subject: [PATCH 2/3] 25.11 fb asb request copy from (#1651)
* Modified metadata to allow copy from process to
allow program to structurally display data
into column order from left to right.
* Modified metadata to allow copy from process to
allow program to structurally display data
into column order from left to right.
---
.../onprc_ehr/model/sources/ASB_Services.js | 35 +++----------------
1 file changed, 4 insertions(+), 31 deletions(-)
diff --git a/onprc_ehr/resources/web/onprc_ehr/model/sources/ASB_Services.js b/onprc_ehr/resources/web/onprc_ehr/model/sources/ASB_Services.js
index 45ff89712..b4bdc0be1 100644
--- a/onprc_ehr/resources/web/onprc_ehr/model/sources/ASB_Services.js
+++ b/onprc_ehr/resources/web/onprc_ehr/model/sources/ASB_Services.js
@@ -29,7 +29,7 @@ EHR.model.DataModelManager.registerMetadata('ASB_Services', {
'study.encounters': {
chargetype: {
defaultValue: 'DCM: ASB Services',
- hidden: true
+ hidden: false
},
date: {
xtype: 'xdatetime',
@@ -89,7 +89,7 @@ EHR.model.DataModelManager.registerMetadata('ASB_Services', {
'study.blood': {
chargetype: {
defaultValue: 'DCM: ASB Services',
- hidden: true
+ hidden: false
},
performedby: {
//defaultValue: LABKEY.Security.currentUser.displayName,
@@ -105,7 +105,7 @@ EHR.model.DataModelManager.registerMetadata('ASB_Services', {
'study.drug': {
chargetype: {
defaultValue: 'DCM: ASB Services',
- hidden: true
+ hidden: false
},
date: {
xtype: 'xdatetime',
@@ -147,33 +147,6 @@ EHR.model.DataModelManager.registerMetadata('ASB_Services', {
}
}
- // Modified: 7-27-2017 R.Blasa not needed for this version
- //'study.treatment_order': {
- // chargetype: {
- // defaultValue: 'DCM: ASB Services',
- // hidden: true
- // },
- // date: {
- // defaultValue: new Date()
- // },
- // Billable: {
- // defaultValue: 'Yes',
- // hidden: true
- // },
- // code: {
- // header: 'Agent',
- // editorConfig: {
- // defaultSubset: 'Research'
- // }
- // },
- // category: {
- // defaultValue: 'Research',
- // hidden: true
- // },
- // remark: {
- // header: 'Special Instructions',
- // hidden: false
- // }
- //}
+
}
});
\ No newline at end of file
From 1d444b0620a4ed68586a3a07830a41bc4dfe10d9 Mon Sep 17 00:00:00 2001
From: Ohsudev <76500320+Ohsudev@users.noreply.github.com>
Date: Mon, 16 Mar 2026 10:16:50 -0700
Subject: [PATCH 3/3] =?UTF-8?q?Modified=20Research=20Procedure=20form=20to?=
=?UTF-8?q?=20auto=20load=20Charge=20unit=20when=20select=E2=80=A6=20(#165?=
=?UTF-8?q?0)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Modified Research Procedure form to auto load Charge unit when selecting "TMB breeding" procedure.
* Modified Research Procedure form to auto load Charge unit when selecting "TMB breeding" procedure.
---
.../sources/ClinicalEncountersClientStore.js | 122 ++++++++++++++++++
.../ClinicalEncountersFormSection.java | 4 +-
2 files changed, 124 insertions(+), 2 deletions(-)
create mode 100644 onprc_ehr/resources/web/onprc_ehr/data/sources/ClinicalEncountersClientStore.js
diff --git a/onprc_ehr/resources/web/onprc_ehr/data/sources/ClinicalEncountersClientStore.js b/onprc_ehr/resources/web/onprc_ehr/data/sources/ClinicalEncountersClientStore.js
new file mode 100644
index 000000000..9c6d90360
--- /dev/null
+++ b/onprc_ehr/resources/web/onprc_ehr/data/sources/ClinicalEncountersClientStore.js
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013-2019 LabKey Corporation
+ *
+ * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+ */
+Ext4.define('ONPRC_EHR.data.ClinicalEncountersClientStore', {
+ extend: 'EHR.data.DataEntryClientStore',
+
+ constructor: function(){
+ this.callParent(arguments);
+
+ this.on('add', this.onAddRecord, this);
+ },
+
+ onAddRecord: function(store, records){
+ Ext4.each(records, function(record){
+ this.onRecordUpdate(record, ['procedureid']);
+ }, this);
+ },
+
+ afterEdit: function(record, modifiedFieldNames){
+ this.onRecordUpdate(record, modifiedFieldNames);
+
+ this.callParent(arguments);
+ },
+
+ onRecordUpdate: function(record, modifiedFieldNames){
+ if (record.get('procedureid')){
+ modifiedFieldNames = modifiedFieldNames || [];
+
+ var lookupRec = this.getProcedureRecord(record.get('procedureid'));
+ if (!lookupRec)
+ return;
+
+ if (lookupRec.get('remark')&& record.get('remark')== null){
+ record.beginEdit();
+ record.set('remark', lookupRec.get('remark'));
+ record.endEdit(true);
+ }
+
+ if (lookupRec.get('name')&& lookupRec.get('name')== 'TMB breeding'){
+ record.beginEdit();
+ record.set('chargetype', 'TMB');
+ record.endEdit(true);
+ }
+ }
+
+ if (modifiedFieldNames && (modifiedFieldNames.indexOf('Id') > -1 || modifiedFieldNames.indexOf('project') > -1 || modifiedFieldNames.indexOf('chargetype') > -1)){
+ if (record.get('objectid')){
+ var toApply = {
+ Id: record.get('Id'),
+ project: record.get('project'),
+ chargetype: record.get('chargetype')
+ };
+
+ this.storeCollection.clientStores.each(function(cs){
+ if (cs.storeId == this.storeCollection.collectionId + '-' + 'encounters'){
+ return;
+ }
+
+ var projectField = cs.getFields().get('project');
+ var chargeTypeField = cs.getFields().get('chargetype');
+ var hasChanges = false;
+
+ if (cs.getFields().get('parentid')){
+ if (cs.getFields().get('Id') || cs.getFields().get('project')){
+ cs.each(function(r){
+ if (r.get('parentid') === record.get('objectid')){
+ var obj = {};
+
+ if (projectField){
+ if (!r.get('project') || (projectField.inheritFromParent && r.get('project') !== record.get('project'))){
+ obj.project = record.get('project');
+ }
+ }
+
+ if (chargeTypeField){
+ if (!r.get('chargetype') || (chargeTypeField.inheritFromParent && r.get('chargetype') !== record.get('chargetype'))){
+ obj.chargetype = record.get('chargetype');
+ }
+ }
+
+ if (r.get('Id') !== record.get('Id')){
+ obj.Id = record.get('Id');
+ }
+
+ if (!Ext4.Object.isEmpty(obj)){
+ r.beginEdit();
+ r.set(obj);
+ r.endEdit(true);
+ hasChanges = true;
+ }
+ }
+ }, this);
+ }
+ }
+
+ if (hasChanges){
+ cs.fireEvent('datachanged', cs);
+ }
+ }, this);
+ }
+ }
+ },
+
+ getProcedureRecord: function(procedureId){
+ var procedureStore = EHR.DataEntryUtils.getProceduresStore();
+ LDK.Assert.assertNotEmpty('Unable to find procedureStore from ClinicalEncountersClientStore', procedureStore);
+
+ // If the store is not loaded this will call again. No need to do a load callback here just return undefined if not loaded.
+ if (LABKEY.ext4.Util.hasStoreLoaded(procedureStore)) {
+ var procRecIdx = procedureStore.findExact('rowid', procedureId);
+ LDK.Assert.assertTrue('Unable to find procedure record in ClinicalEncountersClientStore for procedureId: [' + procedureId + ']', procRecIdx > -1);
+
+ var procedureRec = procedureStore.getAt(procRecIdx);
+ LDK.Assert.assertNotEmpty('Unable to find procedure record from ClinicalEncountersClientStore. ProcedureId was: [' + procedureId + ']', procedureRec);
+
+ return procedureRec;
+ }
+ return undefined;
+ }
+});
\ No newline at end of file
diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalEncountersFormSection.java b/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalEncountersFormSection.java
index 90dafd050..98246d927 100644
--- a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalEncountersFormSection.java
+++ b/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalEncountersFormSection.java
@@ -33,8 +33,8 @@ public ClinicalEncountersFormSection()
{
super("study", "encounters", "Procedures", "ehr-gridpanel", EHRService.FORM_SECTION_LOCATION.Body);
addClientDependency(ClientDependency.supplierFromPath("ehr/buttons/encounterButtons.js"));
- addClientDependency(ClientDependency.supplierFromPath("ehr/data/ClinicalEncountersClientStore.js"));
- setClientStoreClass("EHR.data.ClinicalEncountersClientStore");
+ addClientDependency(ClientDependency.supplierFromPath("onprc_ehr/data/sources/ClinicalEncountersClientStore.js"));
+ setClientStoreClass("ONPRC_EHR.data.ClinicalEncountersClientStore");
//Modified 8-24-2015 Blasa Shows Template menus
// setTemplateMode(TEMPLATE_MODE.NONE);