Skip to content

Commit a1de14b

Browse files
Disable Id on Exam forms when case created. (#1707)
* Disable the Id field if there is already a caseid in the record * Disable the Id field when the 'casecreated' event is fired * Remove duplicate remark metadata * Remove CagemateClinicalReportFormType as it is unused * Create automated tests
1 parent 13ea546 commit a1de14b

6 files changed

Lines changed: 216 additions & 77 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2025 LabKey Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
EHR.model.DataModelManager.registerMetadata('CaseMgmt', {
7+
byQuery: {
8+
'study.clinremarks': {
9+
Id: {
10+
columnConfig: {
11+
getEditor: function(rec){
12+
if (rec && rec.get('caseid')){
13+
return false;
14+
}
15+
return {
16+
xtype: 'ehr-animalfield',
17+
dataIndex: 'Id'
18+
};
19+
}
20+
},
21+
formEditorConfig: {
22+
listeners: {
23+
afterrender: function(field){
24+
var TOOLTIP = 'Refresh the form to enter data for a different animal.';
25+
var syncDisabledStyle = function(readOnly){
26+
var inputEl = field.inputEl;
27+
if (inputEl){
28+
inputEl.setStyle({
29+
'background-color': readOnly ? '#f0f0f0' : '',
30+
color: readOnly ? '#666666' : '',
31+
cursor: readOnly ? 'not-allowed' : ''
32+
});
33+
}
34+
};
35+
var setTooltip = function(readOnly){
36+
var el = field.getEl();
37+
if (el){
38+
el.set({'data-qtip': readOnly ? TOOLTIP : ''});
39+
}
40+
};
41+
var syncReadOnly = function(){
42+
var rec = EHR.DataEntryUtils.getBoundRecord(field);
43+
var readOnly = !!(rec && rec.get('caseid'));
44+
field.setReadOnly(readOnly);
45+
syncDisabledStyle(readOnly);
46+
setTooltip(readOnly);
47+
};
48+
syncReadOnly();
49+
var formPanel = field.up('ehr-formpanel');
50+
if (formPanel){
51+
field.mon(formPanel, 'bindrecord', syncReadOnly, field, {buffer: 50});
52+
}
53+
if (EHR.DemographicsCache){
54+
field.mon(EHR.DemographicsCache, 'casecreated', function(animalId){
55+
var rec = EHR.DataEntryUtils.getBoundRecord(field);
56+
if (rec && rec.get('Id') === animalId){
57+
field.setReadOnly(true);
58+
syncDisabledStyle(true);
59+
setTooltip(true);
60+
}
61+
}, field);
62+
}
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
});

onprc_ehr/resources/web/onprc_ehr/model/sources/ClinicalReport.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ EHR.model.DataModelManager.registerMetadata('ClinicalReport_ONPRC', {
2727
hidden: false,
2828
allowBlank: false
2929
},
30-
remark: {
31-
hidden: false
32-
},
3330
p2: {
3431
formEditorConfig: {
3532
xtype: 'ehr-plantextarea'

onprc_ehr/src/org/labkey/onprc_ehr/dataentry/BehaviorExamFormType.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public BehaviorExamFormType(DataEntryFormContext ctx, Module owner)
5858
{
5959
s.addConfigSource("BehaviorDefaults");
6060

61+
if (s.getName().equals("Clinical Remarks"))
62+
s.addConfigSource("CaseMgmt");
63+
6164
if (!s.getName().equals("Clinical Remarks"))
6265
s.addConfigSource("ClinicalReportChild");
6366

@@ -78,6 +81,7 @@ public BehaviorExamFormType(DataEntryFormContext ctx, Module owner)
7881
addClientDependency(ClientDependency.supplierFromPath("ehr/model/sources/ClinicalReport.js"));
7982
addClientDependency(ClientDependency.supplierFromPath("ehr/panel/ExamDataEntryPanel.js"));
8083
addClientDependency(ClientDependency.supplierFromPath("ehr/model/sources/ClinicalReportChild.js"));
84+
addClientDependency(ClientDependency.supplierFromPath("onprc_ehr/model/sources/CaseMgmt.js"));
8185
setJavascriptClass("EHR.panel.ExamDataEntryPanel");
8286

8387
// //Added: 12-18-2017 R.Blasa

onprc_ehr/src/org/labkey/onprc_ehr/dataentry/CagemateClinicalReportFormType.java

Lines changed: 0 additions & 74 deletions
This file was deleted.

onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalReportFormType.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public ClinicalReportFormType(DataEntryFormContext ctx, Module owner)
7676
// s.addConfigSource("ClinicalReport");
7777
s.addConfigSource("ClinicalReport_ONPRC");
7878

79+
if (s.getName().equals("Clinical Remarks"))
80+
s.addConfigSource("CaseMgmt");
81+
7982
if (!s.getName().equals("Clinical Remarks"))
8083
s.addConfigSource("ClinicalReportChild");
8184

@@ -95,6 +98,7 @@ public ClinicalReportFormType(DataEntryFormContext ctx, Module owner)
9598

9699
// Modified: 10-5-2017 R.Blasa reinstalled 2-12-21
97100
addClientDependency(ClientDependency.supplierFromPath("onprc_ehr/model/sources/ClinicalReport.js"));
101+
addClientDependency(ClientDependency.supplierFromPath("onprc_ehr/model/sources/CaseMgmt.js"));
98102

99103

100104
// Added: 7-22-2025 R.Blasa

onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest2.java

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,6 +1449,145 @@ public void gridErrorsTest()
14491449
//TODO: make sure fields turn red as expected
14501450
}
14511451

1452+
/**
1453+
* Verifies the clinremarks Id field read-only + tooltip behavior wired in via the
1454+
* `CaseMgmt` metadata source (onprc_ehr/model/sources/CaseMgmt.js). The source is
1455+
* registered against both ClinicalReportFormType and BehaviorExamFormType, so this
1456+
* test exercises both forms through a shared helper.
1457+
*/
1458+
@Test
1459+
public void testClinremarksIdReadOnlyOnCaseCreated() throws Exception
1460+
{
1461+
goToEHRFolder();
1462+
1463+
// Verify case open and open & immediately close. Test Exams/Cases and BSU Exam forms.
1464+
verifyClinremarksIdLockOnCaseCreated("Exams/Cases", "Clinical", "Open Case", CaseEntryPath.MANAGE_CASES_LINK);
1465+
verifyClinremarksIdLockOnCaseCreated("BSU Exam", "Behavior", "Open & Immediately Close", CaseEntryPath.MANAGE_CASES_LINK);
1466+
1467+
// Top and bottom buttons
1468+
verifyClinremarksIdLockOnCaseCreated("Exams/Cases", "Clinical", "Open Case", CaseEntryPath.OPEN_MANAGE_TOP);
1469+
verifyClinremarksIdLockOnCaseCreated("BSU Exam", "Behavior", "Open Case", CaseEntryPath.OPEN_MANAGE_BOTTOM);
1470+
}
1471+
1472+
/** How the test reaches the "create new case" dialog from the data entry form. */
1473+
private enum CaseEntryPath
1474+
{
1475+
/** [Manage Cases] link → "Open Case" split button → "Open <category> Case" menu item. */
1476+
MANAGE_CASES_LINK,
1477+
/** "Open/Manage <category> Case" button in the Instructions panel at the top of the form. */
1478+
OPEN_MANAGE_TOP,
1479+
/** "Open/Manage <category> Case" button in the form's docked footer toolbar. */
1480+
OPEN_MANAGE_BOTTOM
1481+
}
1482+
1483+
private void verifyClinremarksIdLockOnCaseCreated(String formLinkLabel, String caseCategory, String openButtonLabel, CaseEntryPath entryPath)
1484+
{
1485+
final String expectedTooltip = "Refresh the form to enter data for a different animal.";
1486+
1487+
log("Verifying clinremarks Id read-only on casecreated for form: " + formLinkLabel + " via '" + openButtonLabel + "' (entry: " + entryPath + ")");
1488+
_helper.goToTaskForm(formLinkLabel, false);
1489+
_ext4Helper.clickExt4Tab("SOAP");
1490+
1491+
Ext4FieldRef idField = _helper.getExt4FieldForFormSection("SOAP", "Id");
1492+
Assert.assertNotNull("Could not locate Id field in SOAP section of form: " + formLinkLabel, idField);
1493+
1494+
idField.setValue(SUBJECTS[0]);
1495+
1496+
// Wait for the AnimalDetailsPanel to display the Id
1497+
Ext4FieldRef detailsId = _ext4Helper.queryOne("displayfield[name=animalId]", Ext4FieldRef.class);
1498+
Assert.assertNotNull("AnimalDetailsPanel not rendered (form: " + formLinkLabel + ")", detailsId);
1499+
waitFor(() -> SUBJECTS[0].equals(String.valueOf(detailsId.getValue())),
1500+
"AnimalDetailsPanel did not display Id " + SUBJECTS[0] + " (form: " + formLinkLabel + ")",
1501+
WAIT_FOR_JAVASCRIPT);
1502+
1503+
Assert.assertNotEquals("Id field should be editable before any case is created (form: " + formLinkLabel + ")",
1504+
Boolean.TRUE, idField.getEval("readOnly"));
1505+
Object preQtip = idField.getEval("getEl().dom.getAttribute('data-qtip')");
1506+
assertEquals("Id field should have no tooltip before any case is created (form: " + formLinkLabel + ")",
1507+
"", preQtip == null ? "" : preQtip.toString());
1508+
1509+
// Open a real case from the data entry form so ManageCasesPanel fires the real casecreated event
1510+
createCaseFromForm(SUBJECTS[0], caseCategory, openButtonLabel, entryPath);
1511+
1512+
waitFor(() -> Boolean.TRUE.equals(idField.getEval("readOnly")),
1513+
"Id field did not become read-only after case was opened (form: " + formLinkLabel + ")",
1514+
WAIT_FOR_JAVASCRIPT);
1515+
Object postQtip = idField.getEval("getEl().dom.getAttribute('data-qtip')");
1516+
assertEquals("Id field should carry the lock tooltip after case is opened (form: " + formLinkLabel + ")",
1517+
expectedTooltip, postQtip == null ? "" : postQtip.toString());
1518+
1519+
_helper.discardForm();
1520+
}
1521+
1522+
/**
1523+
* Opens a case for {@code animalId} from the data entry form via the given {@code entryPath}, then
1524+
* closes the resulting Manage Cases window. {@code openButtonLabel} is one of the OpenCaseWindow submit
1525+
* buttons: "Open Case" (just open) or "Open & Immediately Close" (open and immediately close permanently).
1526+
*/
1527+
private void createCaseFromForm(String animalId, String caseCategory, String openButtonLabel, CaseEntryPath entryPath)
1528+
{
1529+
Locator.XPathLocator manageCasesWindow = Ext4Helper.Locators.window("Manage Cases: " + animalId);
1530+
triggerCreateCaseDialog(manageCasesWindow, caseCategory, entryPath);
1531+
1532+
// ManageCasesPanel asks "Open New" vs "Edit Existing" first if an active case of this category
1533+
// already exists for the animal (ManageCasesPanel.js: showCreateWindow). Dismiss with "Open New".
1534+
Locator.XPathLocator existingCaseDialog = Ext4Helper.Locators.window("Open Case");
1535+
if (Boolean.TRUE.equals(waitFor(() -> isElementPresent(existingCaseDialog), 2000)))
1536+
{
1537+
waitAndClick(existingCaseDialog.append(Ext4Helper.Locators.ext4ButtonEnabled("Open New")));
1538+
waitForElementToDisappear(existingCaseDialog);
1539+
}
1540+
1541+
Locator.XPathLocator openCaseWindow = Ext4Helper.Locators.window("Open Case: " + animalId);
1542+
waitForElement(openCaseWindow);
1543+
1544+
if ("Clinical".equals(caseCategory))
1545+
{
1546+
Ext4ComboRef vetField = Ext4ComboRef.getForLabel(this, "Assigned Vet");
1547+
vetField.waitForStoreLoad();
1548+
// Pick whichever vet the store has rather than hardcoding a display name; the test only needs the field populated
1549+
vetField.eval("setValue(arguments[0])", vetField.getFnEval("return this.store.getAt(0).get(this.valueField)"));
1550+
Ext4FieldRef.getForLabel(this, "Problem").setValue("Behavioral");
1551+
}
1552+
else if ("Behavior".equals(caseCategory))
1553+
{
1554+
Ext4FieldRef.getForLabel(this, "Subcategory").setValue("Alopecia");
1555+
}
1556+
1557+
waitAndClick(openCaseWindow.append(Ext4Helper.Locators.ext4ButtonEnabled(openButtonLabel)));
1558+
if ("Open & Immediately Close".equals(openButtonLabel))
1559+
{
1560+
waitAndClick(Ext4Helper.Locators.menuItem("Close Permanently").notHidden());
1561+
}
1562+
waitForElementToDisappear(openCaseWindow);
1563+
1564+
waitAndClick(manageCasesWindow.append(Ext4Helper.Locators.ext4ButtonEnabled("Close")));
1565+
waitForElementToDisappear(manageCasesWindow);
1566+
}
1567+
1568+
private void triggerCreateCaseDialog(Locator.XPathLocator manageCasesWindow, String caseCategory, CaseEntryPath entryPath)
1569+
{
1570+
switch (entryPath)
1571+
{
1572+
case MANAGE_CASES_LINK -> {
1573+
waitAndClick(Locator.linkWithText("[Manage Cases]"));
1574+
waitForElement(manageCasesWindow);
1575+
waitAndClick(manageCasesWindow.append(Ext4Helper.Locators.ext4ButtonEnabled("Open Case")));
1576+
waitAndClick(Ext4Helper.Locators.menuItem("Open " + caseCategory + " Case").notHidden());
1577+
}
1578+
case OPEN_MANAGE_TOP -> {
1579+
Locator.XPathLocator instructionsPanel = Locator.tagWithClass("div", "x4-panel").withChild(
1580+
Locator.tagWithClass("div", "x4-panel-header").withDescendant(
1581+
Locator.tagWithClass("span", "x4-panel-header-text").withText("Instructions")));
1582+
waitAndClick(instructionsPanel.append(Ext4Helper.Locators.ext4ButtonEnabled("Open/Manage " + caseCategory + " Case")));
1583+
}
1584+
case OPEN_MANAGE_BOTTOM -> {
1585+
Locator.XPathLocator footerToolbar = Locator.tagWithClass("div", "x4-toolbar-footer");
1586+
waitAndClick(footerToolbar.append(Ext4Helper.Locators.ext4ButtonEnabled("Open/Manage " + caseCategory + " Case")));
1587+
}
1588+
}
1589+
}
1590+
14521591
@Override
14531592
protected String getAnimalHistoryPath()
14541593
{

0 commit comments

Comments
 (0)