@@ -1225,6 +1225,61 @@ public void testDeathNecropsyForm() throws IOException, CommandException
12251225
12261226 }
12271227
1228+ @ Test
1229+ public void testDeathAndDepartureBlockedByReviewRequiredData () throws Exception
1230+ {
1231+ String animalId = "REVREQ1" ;
1232+ String taskId = UUID .randomUUID ().toString ();
1233+
1234+ goToEHRFolder ();
1235+
1236+ log ("Creating a live animal with a weight record in Review Required state" );
1237+ getApiHelper ().deleteAllRecords ("study" , "deaths" , new Filter ("Id" , animalId ));
1238+ getApiHelper ().deleteAllRecords ("study" , "departure" , new Filter ("Id" , animalId ));
1239+ getApiHelper ().deleteAllRecords ("study" , "weight" , new Filter ("Id" , animalId ));
1240+ getApiHelper ().deleteAllRecords ("study" , "demographics" , new Filter ("Id" , animalId ));
1241+
1242+ String [] demographicsFields = {"Id" , "Species" , "Birth" , "Gender" , "date" , "calculated_status" , "objectid" , "performedby" };
1243+ Object [][] demographicsData = {{animalId , "Rhesus" , (new Date ()).toString (), getMale (), new Date (), "Alive" , UUID .randomUUID ().toString (), 1004 }};
1244+ getApiHelper ().doSaveRows (DATA_ADMIN .getEmail (), getApiHelper ().prepareInsertCommand ("study" , "demographics" , "lsid" , demographicsFields , demographicsData ), getExtraContext ());
1245+
1246+ // The weight record carries the same taskid used by the death/departure rows below so the exclusion
1247+ // scenarios can prove that records belonging to the completing record's own task are excluded from
1248+ // the review-required check. Both scenarios are validate-only, so nothing persists between them.
1249+ String [] weightInsertFields = {"Id" , "date" , "weight" , "taskid" , FIELD_QCSTATELABEL , "performedby" };
1250+ Object [][] weightInsertData = {{animalId , new Date (), 8.5 , taskId , EHRQCState .REVIEW_REQUIRED .label , 1004 }};
1251+ getApiHelper ().doSaveRows (DATA_ADMIN .getEmail (), getApiHelper ().prepareInsertCommand ("study" , "weight" , "lsid" , weightInsertFields , weightInsertData ), getExtraContext ());
1252+
1253+ String [] deathFields = {"Id" , "date" , "reason" , "deathWeight" , "taskid" , FIELD_QCSTATELABEL , FIELD_OBJECTID , FIELD_LSID , "_recordid" , "performedby" };
1254+ verifyCompletionBlockedByReviewRequired ("deaths" , "Death" , deathFields ,
1255+ new Object []{animalId , new Date (), "4" , 8.5 , null , EHRQCState .COMPLETED .label , null , null , "recordID" , 1004 },
1256+ new Object []{animalId , new Date (), "4" , 8.5 , taskId , EHRQCState .COMPLETED .label , null , null , "recordID" , 1004 });
1257+
1258+ String [] departureFields = {"Id" , "date" , "destination" , "taskid" , FIELD_QCSTATELABEL , FIELD_OBJECTID , FIELD_LSID , "_recordid" , "performedby" };
1259+ verifyCompletionBlockedByReviewRequired ("departure" , "Departure" , departureFields ,
1260+ new Object []{animalId , new Date (), "Oregon NPRC" , null , EHRQCState .COMPLETED .label , null , null , "recordID" , 1004 },
1261+ new Object []{animalId , new Date (), "Oregon NPRC" , taskId , EHRQCState .COMPLETED .label , null , null , "recordID" , 1004 });
1262+ }
1263+
1264+ // Asserts that completing a record (death, departure) is blocked while other data for the animal is in
1265+ // Review Required state, and that a record whose taskid matches the review-required data is NOT blocked
1266+ // (its records move to Completed in the same save). rowWithoutTaskId/rowWithTaskId differ only in taskid.
1267+ private void verifyCompletionBlockedByReviewRequired (String queryName , String recordNoun , String [] fields , Object [] rowWithoutTaskId , Object [] rowWithTaskId )
1268+ {
1269+ // testValidationMessage defaults extraContext.targetQC to 'In Progress', and the global targetQC always
1270+ // overrides row-level QCStateLabel (see ehr/security.js normalizeQCState), so it must be forced to
1271+ // 'Completed' for the trigger to see a completing record.
1272+ Map <String , Object > completedTargetQC = Map .of ("targetQC" , EHRQCState .COMPLETED .label );
1273+
1274+ log ("Completing a " + queryName + " record while other data is in Review Required state should be blocked" );
1275+ Map <String , List <String >> expected = new HashMap <>();
1276+ expected .put ("Id" , Collections .singletonList ("ERROR: " + recordNoun + " record cannot be completed. There is still data in Review Required state for this animal in the following dataset(s): Weight" ));
1277+ getApiHelper ().testValidationMessage (DATA_ADMIN .getEmail (), "study" , queryName , fields , new Object [][]{rowWithoutTaskId }, expected , completedTargetQC );
1278+
1279+ log ("Review Required records on the " + queryName + " record's own task should not block completion" );
1280+ getApiHelper ().testValidationMessage (DATA_ADMIN .getEmail (), "study" , queryName , fields , new Object [][]{rowWithTaskId }, new HashMap <>(), completedTargetQC );
1281+ }
1282+
12281283 @ Test
12291284 public void testClinicalCasesWorkflow ()
12301285 {
0 commit comments