diff --git a/ONPRC_EHR_ComplianceDB/resources/queries/EmployeeData/ComplianceProcedureRecentTests/.qview.xml b/ONPRC_EHR_ComplianceDB/resources/queries/EmployeeData/ComplianceProcedureRecentTests/.qview.xml new file mode 100644 index 000000000..c2b48d9ac --- /dev/null +++ b/ONPRC_EHR_ComplianceDB/resources/queries/EmployeeData/ComplianceProcedureRecentTests/.qview.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ONPRC_EHR_ComplianceDB/resources/views/employeeDetailsPublic.html b/ONPRC_EHR_ComplianceDB/resources/views/employeeDetailsPublic.html new file mode 100644 index 000000000..fd1dbddf8 --- /dev/null +++ b/ONPRC_EHR_ComplianceDB/resources/views/employeeDetailsPublic.html @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/ONPRC_EHR_ComplianceDB/resources/views/employeeDetailsPublic.view.xml b/ONPRC_EHR_ComplianceDB/resources/views/employeeDetailsPublic.view.xml new file mode 100644 index 000000000..2d2c973a7 --- /dev/null +++ b/ONPRC_EHR_ComplianceDB/resources/views/employeeDetailsPublic.view.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/onprc_billing/resources/queries/onprc_billing/leaseFees.sql b/onprc_billing/resources/queries/onprc_billing/leaseFees.sql index bb5c09ce8..f427f4157 100644 --- a/onprc_billing/resources/queries/onprc_billing/leaseFees.sql +++ b/onprc_billing/resources/queries/onprc_billing/leaseFees.sql @@ -56,8 +56,7 @@ When a5.id is not Null then (Select c.rowid from Site.{substitutePath moduleProperty('ONPRC_Billing','BillingContainer')}.onprc_billing.chargeableItems c where c.itemCode = 'ONR41') When (a4.id is not null and (TIMESTAMPDIFF('SQL_TSI_Day',a.date,a.projectedRelease)) <=14 and a.endDate is null and a.ageAtTime.AgeAtTimeYearsRounded < 1) then (Select c.rowid from Site.{substitutePath moduleProperty('ONPRC_Billing','BillingContainer')}.onprc_billing.chargeableItems c where c.itemCode = 'ONR44') - WHEN (a.duration <= CAST(javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_MAX_DURATION') as INTEGER) AND a.enddate IS NOT NULL AND a.assignCondition = a.releaseCondition) THEN (SELECT rowid FROM onprc_billing_public.chargeableItems ci WHERE ci.active = true AND ci.name = javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_NAME')) - WHEN (a.duration <= CAST(javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_MAX_DURATION') as INTEGER) AND a.enddate IS NOT NULL AND a.assignCondition = a.releaseCondition) THEN (SELECT rowid FROM onprc_billing_public.chargeableItems ci WHERE ci.active = true AND ci.name = javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_NAME')) + WHEN (a.duration <= CAST(javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_MAX_DURATION') as INTEGER) AND a.enddate IS NOT NULL AND a.assignCondition = a.releaseCondition) THEN (SELECT rowid FROM onprc_billing_public.chargeableItems ci WHERE ci.active = true AND ci.name = javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_NAME')) WHEN a2.id IS NOT NULL THEN (SELECT rowid FROM onprc_billing_public.chargeableItems ci WHERE (ci.startDate <= a.date and ci.endDate >= a.date) AND ci.name = javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.TMB_LEASE_NAME')) ELSE lf.chargeId END as chargeId, @@ -81,7 +80,8 @@ CASE where b.id = a.id and a1.project.protocol = a2.project.protocol) > 0 THEN 0 WHEN (a.duration = 0 AND a.enddate IS NOT NULL AND a.assignCondition = a.releaseCondition) THEN 1 - WHEN (fl.id Is Not Null) THEN 0 + -- Exempt PI-purchased NHPs from lease fees + WHEN (fl.id IS NOT NULL) THEN 0 --This will check for infants born to resource moms and will not charge WHEN (a.duration <= CAST(javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_MAX_DURATION') as INTEGER) AND a.enddate IS NOT NULL AND a.assignCondition = a.releaseCondition) THEN a.duration @@ -139,21 +139,20 @@ LEFT JOIN onprc_billing.leaseFeeDefinition lf ON ( AND lf.active = true ) - - ---adds the reasearch owned animal exemption -LEFT JOIN study.flags fl on - (a.id = fl.id - and fl.flag.code = 4034 - and (a.date >= fl.date and a.date <=COALESCE(fl.enddate,Now()) )) - +-- Add PI-purchased NHP flag data +LEFT JOIN study.flags fl + ON (a.id = fl.id + AND fl.flag.code = 4034 + AND a.date BETWEEN CAST(fl.date AS DATE) AND COALESCE(fl.enddate, now()) + ) WHERE CAST(a.datefinalized AS DATE) >= CAST(STARTDATE as DATE) AND CAST(a.datefinalized AS DATE) <= CAST(ENDDATE as DATE) AND a.qcstate.publicdata = true --and a.participantID.demographics.species.common not in ('Rabbit','Guinea Pigs') ---add setup fees for all starts, except day leases aznd sla +-- Add lease setup fees for all lease starts, except day leases, PI-purchased NHPs, and sla UNION ALL + SELECT a.id, a.date, @@ -168,7 +167,7 @@ SELECT ' ' as ESPFAnimal, 'Lease Setup Fees' as category, (SELECT rowid FROM onprc_billing_public.chargeableItems ci WHERE ci.active = true AND ci.name = javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.LEASE_SETUP_FEES')) as chargeId, - 1 as quantity, + 1 AS quantity, cast(null as integer) as leaseCharge1, cast(null as integer) as leaseCharge2, a.objectid as sourceRecord, @@ -179,11 +178,19 @@ SELECT FROM study.assignment a +-- Add PI-purchased NHP flag data +LEFT JOIN study.flags fl + ON (a.id = fl.id + AND fl.flag.code = 4034 + AND a.date BETWEEN CAST(fl.date AS DATE) AND COALESCE(fl.enddate, now()) + ) + WHERE CAST(a.datefinalized AS DATE) >= CAST(STARTDATE as DATE) AND CAST(a.datefinalized AS DATE) <= CAST(ENDDATE as DATE) AND a.qcstate.publicdata = true --only charge setup fee for leases >24H. note: duration assumes today as end, so exclude null enddates AND ((a.duration > CAST(javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.DAY_LEASE_MAX_DURATION') as INTEGER)) OR ( a.assignCondition != a.releaseCondition AND a.enddate IS NULL)) and a.id.demographics.species Not IN ('Rabbit','Guinea Pigs') +AND fl.id IS NULL -- Exempt PI-purchased NHPs from lease setup fees --add released animals that need adjustments UNION ALL @@ -208,9 +215,9 @@ a5.id as ESPFAnimal, --////This selectes the charge ID to be used (SELECT max(rowid) as rowid FROM onprc_billing_public.chargeableItems ci WHERE ci.name = javaConstant('org.labkey.onprc_billing.ONPRC_BillingManager.LEASE_FEE_ADJUSTMENT') and ci.active = true) as chargeId, CASE - when (fl.id Is Not Null) then 0 - else 1 - end as quantity, + WHEN (fl.id IS NOT NULL) THEN 0 -- Exempt PI-purchased NHPs from lease fees + ELSE 1 +END AS quantity, lf2.chargeId as leaseCharge1, lf.chargeId as leaseCharge2, a.objectid as sourceRecord, @@ -248,11 +255,12 @@ LEFT join assignment_U42ESPF a5 on and a5.dateonly <=a.dateOnly AND a5.endDateCoalesced >= a.dateOnly) ---adds the reasearch owned animal exemption -LEFT JOIN study.flags fl on - (a.id = fl.id - and fl.flag.code = 4034 - and (a.date >= fl.date and a.date <=COALESCE(fl.enddate,Now()) )) +-- Add PI-purchased NHP flag data +LEFT JOIN study.flags fl + ON (a.id = fl.id + AND fl.flag.code = 4034 + AND a.date BETWEEN CAST(fl.date AS DATE) AND COALESCE(fl.enddate, now()) + ) WHERE a.releaseCondition != a.projectedReleaseCondition and (A.id != A5.id or A5.id is Null) diff --git a/onprc_billingpublic/resources/queries/onprc_billing_public/NIHIndustryRates/.qview.xml b/onprc_billingpublic/resources/queries/onprc_billing_public/NIHIndustryRates/.qview.xml index a473db037..684af65eb 100644 --- a/onprc_billingpublic/resources/queries/onprc_billing_public/NIHIndustryRates/.qview.xml +++ b/onprc_billingpublic/resources/queries/onprc_billing_public/NIHIndustryRates/.qview.xml @@ -6,52 +6,51 @@ - + - + - + - + - + - + - + - + - + - diff --git a/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRateSheet/.qview.xml b/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRateSheet/.qview.xml index dfe378d46..cc9151e73 100644 --- a/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRateSheet/.qview.xml +++ b/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRateSheet/.qview.xml @@ -6,52 +6,51 @@ - + - + - + - + - + - + - + - + - + - diff --git a/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRates_ReducedFA/.qview.xml b/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRates_ReducedFA/.qview.xml index a21a4dfe7..cdd6ab9a0 100644 --- a/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRates_ReducedFA/.qview.xml +++ b/onprc_billingpublic/resources/queries/onprc_billing_public/NIHRates_ReducedFA/.qview.xml @@ -6,52 +6,51 @@ - + - + - + - + - + - + - + - + - + - diff --git a/onprc_ehr/resources/queries/ehr_lookups/cageReview.sql b/onprc_ehr/resources/queries/ehr_lookups/cageReview.sql index ab21a94d5..38007587f 100644 --- a/onprc_ehr/resources/queries/ehr_lookups/cageReview.sql +++ b/onprc_ehr/resources/queries/ehr_lookups/cageReview.sql @@ -42,6 +42,7 @@ SELECT t.totalWeightExempt, t.totalHeightExempt + FROM ( SELECT @@ -68,6 +69,8 @@ SELECT sum(CASE WHEN t0.weightExemption IS NULL THEN 0 ELSE 1 END) as totalWeightExempt, count(t0.heightExemption) as totalHeightExempt + + FROM ( SELECT @@ -82,11 +85,14 @@ SELECT group_concat(c1.height) as heights, f.heightExemption, CASE + WHEN hf.Id is not null AND c1.height >= pc.cage_type.height THEN 'ERROR: According to the monkeys weight-- it needs a height step taller than required.' + WHEN hf.Id is not null AND c1.height < pc.cage_type.height THEN 'NOTE: According to the monkeys weight-- it needs a height step taller than required.' WHEN (pc.cage_type.height < c1.height AND f.heightExemption IS NULL) THEN ('ERROR: Insufficient height, ' || h.id ||' needs at least: ' || cast(c1.height AS varchar(50))) WHEN (pc.cage_type.height < c1.height AND f.heightExemption IS NOT NULL) THEN cast(('NOTE: Height Exemption: ' || h.Id) as varchar(500)) ELSE null END as heightStatus, - wf.weightExemption + wf.weightExemption, + hf.height_error FROM ehr_lookups.connectedCages pc @@ -98,17 +104,27 @@ LEFT JOIN ( f.id, min(f.flag.value) as heightExemption FROM study.flags f - WHERE f.isActive = true AND f.flag.category = 'Caging Note' and f.flag.description like '%exempt%' + WHERE f.isActive = true AND f.flag.category = 'Caging Note' and (f.flag.description like '%height-exempt%' or f.flag.description like '%Medical-exempt%') GROUP BY f.Id ) f on (f.Id = h.Id) ---weight flags + --height flags +LEFT JOIN ( + SELECT + f.id, + min(f.flag.value) as height_error + FROM study.flags f + WHERE f.isActive = true AND f.flag.category = 'Caging Note' and (f.flag.description like '%height-error%') + GROUP BY f.Id +) hf on (hf.Id = h.Id) + +---weight flags LEFT JOIN ( SELECT f.id, min(f.flag.value) as weightExemption FROM study.flags f - WHERE f.isActive = true AND f.flag.category = 'Caging Note' and f.flag.description like '%exempt%' + WHERE f.isActive = true AND f.flag.category = 'Caging Note' and (f.flag.description like '%weight-exempt%' or f.flag.description like '%Medical-exempt%') GROUP BY f.Id ) wf on (wf.Id = h.Id) diff --git a/onprc_ehr/resources/queries/onprc_ehr/ChemistryPanicNotification.sql b/onprc_ehr/resources/queries/onprc_ehr/ChemistryPanicNotification.sql index 3118aca4a..dfd49abd0 100644 --- a/onprc_ehr/resources/queries/onprc_ehr/ChemistryPanicNotification.sql +++ b/onprc_ehr/resources/queries/onprc_ehr/ChemistryPanicNotification.sql @@ -14,6 +14,7 @@ select a.Id, from study.ClinpathRuns a, study.chemistryResults b Where a.objectid = b.runid And a.type = 'biochemistry' - And b.qualresult like '%panic%' + And b.qualresult like '%alert%' + And a.qcstate = 18 And b.qcstate = 18 \ No newline at end of file diff --git a/onprc_ehr/resources/queries/study/Demographics/BCS Score.qview.xml b/onprc_ehr/resources/queries/study/Demographics/BCS Score.qview.xml new file mode 100644 index 000000000..5e77609ba --- /dev/null +++ b/onprc_ehr/resources/queries/study/Demographics/BCS Score.qview.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/onprc_ehr/resources/queries/study/birth.query.xml b/onprc_ehr/resources/queries/study/birth.query.xml index 727fb0953..478366b3c 100644 --- a/onprc_ehr/resources/queries/study/birth.query.xml +++ b/onprc_ehr/resources/queries/study/birth.query.xml @@ -51,13 +51,7 @@ - - Dam - - - - - + Birth Record Dam /ehr/participantView.view?participantId=${dam} true diff --git a/onprc_ehr/resources/queries/study/demographicsMostRecentWeight.query.xml b/onprc_ehr/resources/queries/study/demographicsMostRecentWeight.query.xml new file mode 100644 index 000000000..f6db19819 --- /dev/null +++ b/onprc_ehr/resources/queries/study/demographicsMostRecentWeight.query.xml @@ -0,0 +1,35 @@ + + + + + + + true + true + + + Current Weight (kg) + /query/executeQuery.view?schemaName=study& + query.queryName=Weight& + query.id~eq=${id} + + 0.### + + + DateTime + Weight Date + /query/executeQuery.view?schemaName=study& + query.queryName=Weight& + query.id~eq=${id}& + query.date~eq=${MostRecentWeightDate} + + + + true + + + MostRecentWeight +
+
+
+
diff --git a/onprc_ehr/resources/queries/study/demographicsMostRecentWeight.sql b/onprc_ehr/resources/queries/study/demographicsMostRecentWeight.sql new file mode 100644 index 000000000..cbea5887c --- /dev/null +++ b/onprc_ehr/resources/queries/study/demographicsMostRecentWeight.sql @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010-2019 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +SELECT + +w.id, +w.MostRecentWeightDate, +timestampdiff('SQL_TSI_DAY', w.MostRecentWeightDate, now()) AS DaysSinceWeight, + +null as weightField, +--NOTE: we need to be careful in case duplicate weights are entered on the same time +cast(( + SELECT round(cast(AVG(w2.weight) as double), 3) AS _expr + FROM study.weight w2 + WHERE w2.qcstate.publicdata = true AND w.id=w2.id AND w.MostRecentWeightDate=w2.date +) as double) AS MostRecentWeight + +FROM ( +SELECT + w.Id AS Id, + max(w.date) AS MostRecentWeightDate + +FROM study.weight w +WHERE w.qcstate.publicdata = true and w.weight is not null +GROUP BY w.id +) w + +--NOTE: altered to a subselect to avoid duplicate entries from weights with identical time +-- --find the most recent weight associated with that date +-- LEFT JOIN study.weight T2 +-- ON (w.MostRecentWeightDate = t2.date AND w.Id = t2.Id) +-- +-- WHERE t2.qcstate.publicdata = true \ No newline at end of file diff --git a/onprc_ehr/resources/queries/study/demographicsParents.query.xml b/onprc_ehr/resources/queries/study/demographicsParents.query.xml index e3ef57f38..57a19f315 100644 --- a/onprc_ehr/resources/queries/study/demographicsParents.query.xml +++ b/onprc_ehr/resources/queries/study/demographicsParents.query.xml @@ -31,6 +31,17 @@ id
+ + Surrogate Dam + + study + animal + id + + + + Foster Dam Type + Genetic Dam @@ -39,9 +50,17 @@ id + + Genetic Dam Type + Number of Parents Known + + + Observed Dam + + diff --git a/onprc_ehr/resources/queries/study/demographicsParents.sql b/onprc_ehr/resources/queries/study/demographicsParents.sql index 91f7029d2..f282b8bd2 100644 --- a/onprc_ehr/resources/queries/study/demographicsParents.sql +++ b/onprc_ehr/resources/queries/study/demographicsParents.sql @@ -16,7 +16,7 @@ SELECT d.id, - coalesce(p2.parent, b.dam) as dam, + coalesce(p2.parent, b.dam, p5.parent) as dam, CASE WHEN p2.parent IS NOT NULL THEN p2.method WHEN b.dam IS NOT NULL THEN 'Observed' @@ -29,26 +29,31 @@ SELECT WHEN b.sire IS NOT NULL THEN 'Observed' ELSE null END as sireType, - p3.parent as fosterMom, - p3.method as fosterType, + p2.parent as geneticdam, + p2.method as geneticdamtype, + p3.parent as fostermom, + p3.method as fostertype, + p4.parent as surrogatedam, + coalesce(b.dam,p5.parent) as observeddam, (CASE WHEN p3.parent IS NOT NULL THEN 1 ELSE 0 END + + CASE WHEN p4.parent IS NOT NULL THEN 1 ELSE 0 END + CASE WHEN coalesce(p2.parent, b.dam) IS NOT NULL THEN 1 ELSE 0 END + CASE WHEN coalesce(p1.parent, b.sire) IS NOT NULL THEN 1 ELSE 0 END) as numParents, - greatest(d.modified, p1.modified, p2.modified, p3.modified, b.modified) as modified + greatest(d.modified, p1.modified, p2.modified, p3.modified, p4.modified,p5.modified, b.modified) as modified FROM study.demographics d LEFT JOIN ( select p1.id, min(p1.method) as method, max(p1.parent) as parent, max(p1.modified) as modified FROM study.parentage p1 - WHERE (p1.method = 'Genetic' OR p1.method = 'Provisional Genetic') AND p1.relationship = 'Sire' AND p1.enddate IS NULL + WHERE p1.method in ('Genetic','Provisional Genetic') AND p1.relationship = 'Sire' AND p1.enddate IS NULL GROUP BY p1.Id ) p1 ON (d.Id = p1.id) LEFT JOIN ( select p2.id, min(p2.method) as method, max(p2.parent) as parent, max(p2.modified) as modified FROM study.parentage p2 - WHERE (p2.method = 'Genetic' OR p2.method = 'Provisional Genetic') AND p2.relationship = 'Dam' AND p2.enddate IS NULL + WHERE p2.method in ('Genetic','Provisional Genetic') AND p2.relationship = 'Dam' AND p2.enddate IS NULL GROUP BY p2.Id ) p2 ON (d.Id = p2.id) @@ -58,5 +63,19 @@ LEFT JOIN ( WHERE p3.relationship = 'Foster Dam' AND p3.enddate IS NULL GROUP BY p3.Id ) p3 ON (d.Id = p3.id) +LEFT JOIN ( + select p4.id, min(p4.method) as method, max(p4.parent) as parent, max(p4.modified) as modified + FROM study.parentage p4 + WHERE p4.relationship = 'Surrogate Dam' AND p4.enddate IS NULL + GROUP BY p4.Id +) p4 ON (d.Id = p4.id) + +LEFT JOIN ( + select p5.id, min(p5.method) as method, max(p5.parent) as parent, max(p5.modified) as modified + FROM study.parentage p5 + WHERE p5.relationship = 'Observed' AND p5.enddate IS NULL + GROUP BY p5.Id +) p5 ON (d.Id = p5.id) + LEFT JOIN study.birth b ON (b.id = d.id) diff --git a/onprc_ehr/resources/queries/study/demographicsParents/.qview.xml b/onprc_ehr/resources/queries/study/demographicsParents/.qview.xml index 01e76b9af..c55f9e699 100644 --- a/onprc_ehr/resources/queries/study/demographicsParents/.qview.xml +++ b/onprc_ehr/resources/queries/study/demographicsParents/.qview.xml @@ -5,9 +5,13 @@ + + - + + + diff --git a/onprc_ehr/resources/queries/study/housingRoommatesDivider/Caged Housing Only.qview.xml b/onprc_ehr/resources/queries/study/housingRoommatesDivider/Caged Housing Only.qview.xml index 842d92a89..ca53571c1 100644 --- a/onprc_ehr/resources/queries/study/housingRoommatesDivider/Caged Housing Only.qview.xml +++ b/onprc_ehr/resources/queries/study/housingRoommatesDivider/Caged Housing Only.qview.xml @@ -1,6 +1,7 @@ + diff --git a/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-25.004-25.005.sql b/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-25.004-25.005.sql new file mode 100644 index 000000000..6176c6581 --- /dev/null +++ b/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-25.004-25.005.sql @@ -0,0 +1,126 @@ + +CREATE TABLE onprc_ehr.Rpt_TempJmacDate( + searchid integer IDENTITY(100,1) NOT NULL, + animalid varchar(200) NULL, + JBGRemovalDate smalldatetime NULL, + JBGActualRemovalDate smalldatetime NULL + + ) ON [PRIMARY] + GO + +CREATE TABLE onprc_ehr.JmacRemovalDate( + searchid integer IDENTITY(100,1) NOT NULL, + Id varchar(100) NULL, + JBGRemovalDate smalldatetime NULL, + JBGActualRemovalDate smalldatetime NULL, + DaysDiff float NULL, + reason varchar(100) NULL +) ON [PRIMARY] +GO + + + +/* +** +** Created by Date Comment +** +** blasa 4/10/2026 Process to update jmac Removal date +** +** +** +**/ + +CREATE Procedure onprc_ehr.s_JmacRemovalDateProcess + + +AS + + +declare + + + @TempSearchKey Int, + @Searchkey Int, + @AnimalID varchar(100), + @OrgRemovalDate smalldatetime, + @ActualRemovalDate smalldatetime + + +Begin + + + ----- Reset the last two months only + + Delete onprc_ehr.Rpt_TempJmacDate + + If @@Error <> 0 + GoTo Err_Proc + + + + Set @Tempsearchkey = 0 + Set @Searchkey = 0 + Set @Animalid = '' + Set @OrgRemovalDate = null + Set @ActualRemovalDate = null + + --- Set initial processing + + Insert into onprc_ehr.Rpt_TempJmacDate + select Id, JBGRemovalDate, JBGActualRemovalDate + from onprc_ehr.JmacRemovalDate + + Order by searchid + + Select top 1 @SearchKey = searchID from onprc_ehr.Rpt_TempJmacDate + Order by searchid + + + While @Tempsearchkey < @SearchKey + Begin + + Set @Animalid = '' + Set @OrgRemovalDate = null + Set @ActualRemovalDate = null + + select @animalid = animalid, @OrgRemovalDate = JBGRemovalDate, @ActualRemovalDate = JBGActualRemovalDate + from onprc_ehr.Rpt_TempJmacDate Where searchid = @Searchkey + + -------Begin updating records + + + Update JB + Set JB.enddate = @ActualRemovalDate + From StudyDataset.c6d346_animal_group_members JB + Where JB.Participantid = @Animalid + And cast(JB.enddate as Date) = cast(@OrgRemovalDate as Date) + + + If @@Error <> 0 + GoTo Err_Proc + + + Set @TempSearchkey = @Searchkey + + Select Top 1 @SearchKey = searchid From onprc_ehr.Rpt_TempJmacDate + Where searchid > @Tempsearchkey + Order by searchid + + + + + End ------(While @tempsearchkey < @Searchkey) + + + Return 0 + + Err_Proc: Return 1 + + + + +END + + + + diff --git a/onprc_ehr/resources/schemas/onprc_ehr.xml b/onprc_ehr/resources/schemas/onprc_ehr.xml index 298c65edc..9b4e8a75e 100644 --- a/onprc_ehr/resources/schemas/onprc_ehr.xml +++ b/onprc_ehr/resources/schemas/onprc_ehr.xml @@ -1480,4 +1480,25 @@ + + + + + + + +
+ + + + + + + + + + +
+ + diff --git a/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js b/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js index 7628d6ea3..30a298a30 100644 --- a/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js +++ b/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js @@ -1256,85 +1256,118 @@ exports.init = function(EHR){ } } }); + + //Added by Kollil, April 2026 + function toDateOnly(val) { + if (!val) + return null; + + // row.date object + if (typeof val === 'object' && val.time) { + var d = new Date(val.time); + return new Date(d.getFullYear(), d.getMonth(), d.getDate()); + } + + // date strings like "2026-04-20 00:00:00.000" + var s = String(val).split(' ')[0]; // keep only YYYY-MM-DD + var parts = s.split('-'); + if (parts.length !== 3) + return null; + + return new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])); + } + // Added 10-17-2025 R. Blasa EHR.Server.TriggerManager.unregisterAllHandlersForQueryNameAndEvent('study', 'assignment', EHR.Server.TriggerManager.Events.BEFORE_UPSERT); - EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.BEFORE_UPSERT, 'study', 'assignment', function(helper, scriptErrors, row, oldRow){ - if (!helper.isETL()){ + EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.BEFORE_UPSERT, 'study', 'assignment', function(helper, scriptErrors, row, oldRow) { + if (!helper.isETL()) { //note: the the date field is handled above by removeTimeFromDate EHR.Server.Utils.removeTimeFromDate(row, scriptErrors, 'enddate'); EHR.Server.Utils.removeTimeFromDate(row, scriptErrors, 'projectedRelease'); } - //check number of allowed animals at assign/approve time if (!helper.isETL() && !helper.isQuickValidation() && helper.doStandardProtocolCountValidation() && //this is designed to always perform the check on imports, but also updates where the Id was changed - !(oldRow && oldRow.Id && oldRow.Id==row.Id) && + !(oldRow && oldRow.Id && oldRow.Id == row.Id) && row.Id && row.project && row.date - ){ + ) { var assignmentsInTransaction = helper.getProperty('assignmentsInTransaction'); assignmentsInTransaction = assignmentsInTransaction || []; var msgs = helper.getJavaHelper().verifyProtocolCounts(row.Id, row.project, assignmentsInTransaction); - if (msgs){ + if (msgs) { msgs = msgs.split("<>"); - for (var i=0;i row.enddatefinalized.getTime()){ - row.enddatefinalized = row.enddate; - } + // we want to record the date a record was marked endded, in addition to the actual end itself + // NOTE: we only do this when both enddate and releaseType are entered + if (!row.enddatefinalized && row.enddate && row.releaseCondition && EHR.Server.Security.getQCStateByLabel(row.QCStateLabel).PublicData) { + //note: if ended in the future, defer to that date + row.enddatefinalized = new Date(); + if (row.enddate.getTime() > row.enddatefinalized.getTime()) { + row.enddatefinalized = row.enddate; } + } - //check for condition downgrade for assign condition - if (!helper.isETL() && row.Id && row.assignCondition){ - var msg = triggerHelper.checkForConditionDowngrade(row.Id, row.date, row.assignCondition); - if (msg){ - EHR.Server.Utils.addError(scriptErrors, 'assignCondition', msg, 'INFO'); - } + //check for condition downgrade for assign condition + if (!helper.isETL() && row.Id && row.assignCondition) { + var msg = triggerHelper.checkForConditionDowngrade(row.Id, row.date, row.assignCondition); + if (msg) { + EHR.Server.Utils.addError(scriptErrors, 'assignCondition', msg, 'INFO'); } + } - //check for condition downgrade for assign condition - if (!helper.isETL() && row.Id && row.date && row.assignCondition){ - var msg = triggerHelper.checkForConditionDowngrade(row.Id, row.date, row.assignCondition); - if (msg){ - EHR.Server.Utils.addError(scriptErrors, 'assignCondition', msg, 'INFO'); - } + //check for condition downgrade for assign condition + if (!helper.isETL() && row.Id && row.date && row.assignCondition) { + var msg = triggerHelper.checkForConditionDowngrade(row.Id, row.date, row.assignCondition); + if (msg) { + EHR.Server.Utils.addError(scriptErrors, 'assignCondition', msg, 'INFO'); } + } + + //Added by Kollil, April 2026. Refer to ticket # 13807 + if (row.project && row.date) { + var projectEndDate = triggerHelper.getProjectEndDate(row.project); //helper.getJavaHelper().getProjectEndDate(row.project); + var assignmentDate = toDateOnly(row.date); + var endDate = toDateOnly(projectEndDate); + + if (assignmentDate && endDate && assignmentDate.getTime() > endDate.getTime()) { + // console.log('4.5 DEBUG assignmentDate=' + assignmentDate); + EHR.Server.Utils.addError(scriptErrors, 'project', 'The assignment start date occurs after the end date of the selected center project. Please review the assignment date and confirm!', 'WARN'); + + } + } + }); //Added 10-5-2022 R.Blasa @@ -1381,8 +1414,10 @@ exports.init = function(EHR){ // Added: 10-6-2025 EHR.Server.TriggerManager.registerHandlerForQuery(EHR.Server.TriggerManager.Events.AFTER_UPSERT, 'study', 'chemistryResults', function (helper, scriptErrors, row, oldRow) { - if (row.Id && row.qualresult && row.qualresult.indexOf('panic') !== -1) { - console.log("panic values: " + row.qualresult); + + if (row.Id && row.qualresult && row.qualresult.indexOf('alert') !== -1) { + + console.log("alert values: " + row.qualresult); triggerHelper.sendClinpathPanicEmail(row.Id, row.runid, row.objectid); } }); diff --git a/onprc_ehr/resources/views/printRoom.html b/onprc_ehr/resources/views/printRoom.html index 55f2aa8a8..01211fcf8 100644 --- a/onprc_ehr/resources/views/printRoom.html +++ b/onprc_ehr/resources/views/printRoom.html @@ -50,6 +50,7 @@ foundCages = true; Ext4.create('Ext.panel.Panel', { border: false, + width: 1450, defaults: { border: false }, @@ -63,7 +64,7 @@ } else { // Hack to improve printing in Chrome - if (Ext4.isChrome) { Ext4.get(webpart.wrapperDivId).setStyle({zoom: '150%'}); }; + if (Ext4.isChrome) { Ext4.get(webpart.wrapperDivId).setStyle({zoom: '110%'}); }; } }, this); diff --git a/onprc_ehr/resources/web/onprc_ehr/model/sources/BehaviorDefaults.js b/onprc_ehr/resources/web/onprc_ehr/model/sources/BehaviorDefaults.js index 5207f3b7b..eada6c102 100644 --- a/onprc_ehr/resources/web/onprc_ehr/model/sources/BehaviorDefaults.js +++ b/onprc_ehr/resources/web/onprc_ehr/model/sources/BehaviorDefaults.js @@ -117,7 +117,8 @@ EHR.model.DataModelManager.registerMetadata('BehaviorDefaults', { }, category: { lookup: { - filterArray: [LABKEY.Filter.create('category', 'Behavior')] + filterArray: [LABKEY.Filter.create('category', 'Behavior'), + LABKEY.Filter.create('value', 'Behaviors',LABKEY.Filter.Types.NEQ)] } } } diff --git a/onprc_ehr/resources/web/onprc_ehr/model/sources/CaseMgmt.js b/onprc_ehr/resources/web/onprc_ehr/model/sources/CaseMgmt.js new file mode 100644 index 000000000..7a9eb120b --- /dev/null +++ b/onprc_ehr/resources/web/onprc_ehr/model/sources/CaseMgmt.js @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +EHR.model.DataModelManager.registerMetadata('CaseMgmt', { + byQuery: { + 'study.clinremarks': { + Id: { + columnConfig: { + getEditor: function(rec){ + if (rec && rec.get('caseid')){ + return false; + } + return { + xtype: 'ehr-animalfield', + dataIndex: 'Id' + }; + } + }, + formEditorConfig: { + listeners: { + afterrender: function(field){ + var TOOLTIP = 'Refresh the form to enter data for a different animal.'; + var syncDisabledStyle = function(readOnly){ + var inputEl = field.inputEl; + if (inputEl){ + inputEl.setStyle({ + 'background-color': readOnly ? '#f0f0f0' : '', + color: readOnly ? '#666666' : '', + cursor: readOnly ? 'not-allowed' : '' + }); + } + }; + var setTooltip = function(readOnly){ + var el = field.getEl(); + if (el){ + el.set({'data-qtip': readOnly ? TOOLTIP : ''}); + } + }; + var syncReadOnly = function(){ + var rec = EHR.DataEntryUtils.getBoundRecord(field); + var readOnly = !!(rec && rec.get('caseid')); + field.setReadOnly(readOnly); + syncDisabledStyle(readOnly); + setTooltip(readOnly); + }; + syncReadOnly(); + var formPanel = field.up('ehr-formpanel'); + if (formPanel){ + field.mon(formPanel, 'bindrecord', syncReadOnly, field, {buffer: 50}); + } + if (EHR.DemographicsCache){ + field.mon(EHR.DemographicsCache, 'casecreated', function(animalId){ + var rec = EHR.DataEntryUtils.getBoundRecord(field); + if (rec && rec.get('Id') === animalId){ + field.setReadOnly(true); + syncDisabledStyle(true); + setTooltip(true); + } + }, field); + } + } + } + } + } + } + } +}); diff --git a/onprc_ehr/resources/web/onprc_ehr/model/sources/ClinicalReport.js b/onprc_ehr/resources/web/onprc_ehr/model/sources/ClinicalReport.js index 5d2f3e1b5..2b4a9191d 100644 --- a/onprc_ehr/resources/web/onprc_ehr/model/sources/ClinicalReport.js +++ b/onprc_ehr/resources/web/onprc_ehr/model/sources/ClinicalReport.js @@ -27,9 +27,6 @@ EHR.model.DataModelManager.registerMetadata('ClinicalReport_ONPRC', { hidden: false, allowBlank: false }, - remark: { - hidden: false - }, p2: { formEditorConfig: { xtype: 'ehr-plantextarea' diff --git a/onprc_ehr/resources/web/onprc_ehr/panel/RoomLayoutPanel.js b/onprc_ehr/resources/web/onprc_ehr/panel/RoomLayoutPanel.js index 1dd9304b0..b331c6582 100644 --- a/onprc_ehr/resources/web/onprc_ehr/panel/RoomLayoutPanel.js +++ b/onprc_ehr/resources/web/onprc_ehr/panel/RoomLayoutPanel.js @@ -184,15 +184,15 @@ Ext4.define('ONPRC.panel.RoomLayoutPanel', { var rooms = Ext4.Object.getKeys(roomMap).sort(); var dividerWidth = 3; - var height = 115; - var cageWidth = 51; //Modified: 10-15-2020 + var height = 75; + var cageWidth = 60; var hasCages = false; Ext4.each(rooms, function(room, roomIdx){ if (roomIdx == 0 && !config.printMode){ toAdd.push(ONPRC.panel.RoomLayoutPanel.getButtonCfgs(config)); toAdd.push({ - style: 'margin-bottom: 20px;', + style: 'margin-bottom: 5px;', border: false }); } @@ -244,7 +244,7 @@ Ext4.define('ONPRC.panel.RoomLayoutPanel', { border: false }); - //Modified: 8-19-2019 R. Blasa Added Divider Legends Not eeded at this time + //Modified: 8-19-2019 R. Blasa Added Divider Legends Not needed at this time currentSection.push({ //Modified: 4/3/2023 R.Blasa diff --git a/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessing.xlsx b/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessing.xlsx deleted file mode 100644 index 9979a28d5..000000000 Binary files a/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessing.xlsx and /dev/null differ diff --git a/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate.xlsx b/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate.xlsx old mode 100644 new mode 100755 index bae51e026..66f764b22 Binary files a/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate.xlsx and b/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate.xlsx differ diff --git a/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate_TPR_Master.xlsx b/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate_TPR_Master.xlsx old mode 100644 new mode 100755 index d3affcd46..a3ff5ddee Binary files a/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate_TPR_Master.xlsx and b/onprc_ehr/resources/web/onprc_ehr/templates/ClinicalProcessingTemplate_TPR_Master.xlsx differ diff --git a/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing.js b/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing.js index 74cb01ae1..9633f508b 100644 --- a/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing.js +++ b/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing.js @@ -305,6 +305,8 @@ Ext4.define('ONPRC_EHR.window.ClinicalProcessingWindow', { {snomedcode = 'F-31030'} if (row[7]== 3 ) {snomedcode = 'F-31040'} + if (row[7]== 'No' ) + {snomedcode = 'F-30980'} var obj = { Id: id, date: this.getTime(date, times, errors, rowIdx), diff --git a/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing_TPR.js b/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing_TPR.js index 3c7d37472..f883a9b76 100644 --- a/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing_TPR.js +++ b/onprc_ehr/resources/web/onprc_ehr/window/ClinicalMassProcessing_TPR.js @@ -420,6 +420,8 @@ Ext4.define('ONPRC_EHR.window.ClinicalProcessingTPRWindow', { {snomedcode = 'F-31030'} if (row[10]== 3 ) {snomedcode = 'F-31040'} + if (row[10]== 'No' ) + {snomedcode = 'F-30980'} var obj = { Id: id, date: this.getTime(date, times, errors, rowIdx), diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRModule.java b/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRModule.java index d18c157d4..6e766bdc6 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRModule.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRModule.java @@ -124,7 +124,7 @@ public String getName() @Override public @Nullable Double getSchemaVersion() { - return 25.004; + return 25.005; } @Override diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/BehaviorExamFormType.java b/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/BehaviorExamFormType.java index 8f58945e1..843f5e11e 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/BehaviorExamFormType.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/BehaviorExamFormType.java @@ -58,6 +58,9 @@ public BehaviorExamFormType(DataEntryFormContext ctx, Module owner) { s.addConfigSource("BehaviorDefaults"); + if (s.getName().equals("Clinical Remarks")) + s.addConfigSource("CaseMgmt"); + if (!s.getName().equals("Clinical Remarks")) s.addConfigSource("ClinicalReportChild"); @@ -78,6 +81,7 @@ public BehaviorExamFormType(DataEntryFormContext ctx, Module owner) addClientDependency(ClientDependency.supplierFromPath("ehr/model/sources/ClinicalReport.js")); addClientDependency(ClientDependency.supplierFromPath("ehr/panel/ExamDataEntryPanel.js")); addClientDependency(ClientDependency.supplierFromPath("ehr/model/sources/ClinicalReportChild.js")); + addClientDependency(ClientDependency.supplierFromPath("onprc_ehr/model/sources/CaseMgmt.js")); setJavascriptClass("EHR.panel.ExamDataEntryPanel"); // //Added: 12-18-2017 R.Blasa diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/CagemateClinicalReportFormType.java b/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/CagemateClinicalReportFormType.java deleted file mode 100644 index 2040724e2..000000000 --- a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/CagemateClinicalReportFormType.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2013-2016 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.labkey.onprc_ehr.dataentry; - -import org.labkey.api.ehr.dataentry.AbstractFormSection; -import org.labkey.api.ehr.dataentry.DataEntryFormContext; -import org.labkey.api.ehr.dataentry.FormSection; -import org.labkey.api.ehr.dataentry.SimpleGridPanel; -import org.labkey.api.ehr.dataentry.TaskForm; -import org.labkey.api.ehr.dataentry.TaskFormSection; -import org.labkey.api.ehr.security.EHRClinicalEntryPermission; -import org.labkey.api.module.Module; -import org.labkey.api.view.template.ClientDependency; - -import java.util.Arrays; - -//Created: 1-31-2018 R.Blasa - -public class CagemateClinicalReportFormType extends TaskForm -{ - public static final String NAME = "Cagemate Soaps"; - - public CagemateClinicalReportFormType(DataEntryFormContext ctx, Module owner) - { - super(ctx, owner, NAME, "Cagemates Soaps", "Clinical", Arrays.asList( - new TaskFormSection(), - new AnimalDetailsFormSection(), - new SimpleGridPanel("study", "Clinical Remarks", "SOAPs") - - )); - - setStoreCollectionClass("EHR.data.ClinicalReportStoreCollection"); - addClientDependency(ClientDependency.supplierFromPath("ehr/data/ClinicalReportStoreCollection.js")); - - setTemplateMode(AbstractFormSection.TEMPLATE_MODE.NO_ID); - setDisplayReviewRequired(true); - - for (FormSection s : this.getFormSections()) - { - s.addConfigSource("ClinicalDefaults"); - //Added 6-4-2015 Blasa - s.addConfigSource("ClinicalProcedures"); - } - - addClientDependency(ClientDependency.supplierFromPath("ehr/model/sources/ClinicalDefaults.js")); - - - } - - - - @Override - protected boolean canInsert() - { - if (!getCtx().getContainer().hasPermission(getCtx().getUser(), EHRClinicalEntryPermission.class)) - return false; - - return super.canInsert(); - } - -} diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalReportFormType.java b/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalReportFormType.java index fa7b11c04..9a6f1faf4 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalReportFormType.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/dataentry/ClinicalReportFormType.java @@ -76,6 +76,9 @@ public ClinicalReportFormType(DataEntryFormContext ctx, Module owner) // s.addConfigSource("ClinicalReport"); s.addConfigSource("ClinicalReport_ONPRC"); + if (s.getName().equals("Clinical Remarks")) + s.addConfigSource("CaseMgmt"); + if (!s.getName().equals("Clinical Remarks")) s.addConfigSource("ClinicalReportChild"); @@ -95,6 +98,7 @@ public ClinicalReportFormType(DataEntryFormContext ctx, Module owner) // Modified: 10-5-2017 R.Blasa reinstalled 2-12-21 addClientDependency(ClientDependency.supplierFromPath("onprc_ehr/model/sources/ClinicalReport.js")); + addClientDependency(ClientDependency.supplierFromPath("onprc_ehr/model/sources/CaseMgmt.js")); // Added: 7-22-2025 R.Blasa diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java b/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java index fc7964c53..de49e9cc6 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/query/ONPRC_EHRTriggerHelper.java @@ -2021,6 +2021,33 @@ public boolean requiresAssistingStaff(Integer procedureId) return "Surgery".equals(category); } + //Added by Kollil + //Date: Apr 2026 + public Date getProjectEndDate(Object projectId) + { + if (projectId == null) + return null; + + int pid; + if (projectId instanceof Number) + pid = ((Number) projectId).intValue(); + else + pid = Integer.parseInt(projectId.toString()); + + UserSchema ehrSchema = QueryService.get().getUserSchema(_user, _container, "ehr"); + if (ehrSchema == null) + return null; + + TableInfo ti = ehrSchema.getTable("project"); + if (ti == null) + return null; + + SimpleFilter filter = new SimpleFilter(FieldKey.fromString("project"), pid); + TableSelector ts = new TableSelector(ti, Collections.singleton("enddate"), filter, null); + + return ts.getObject(Date.class); + } + public String getSpeciesForDam(String dam) { return new TableSelector(getTableInfo("study", "demographics"), PageFlowUtil.set("species"), new SimpleFilter(FieldKey.fromString("Id"), dam), null).getObject(String.class); @@ -2640,7 +2667,8 @@ public void exec(ResultSet object) throws SQLException public void sendClinpathPanicEmail(String id, String runid, String objectid) { - String subject = "Chemistry Results with Panic values"; + String subject = "Chemistry Results with alert values"; + Container c = getContainer(); @@ -2671,7 +2699,7 @@ public void sendClinpathPanicEmail(String id, String runid, String objectid) if (ts.getRowCount() == 0) { - html.append("There are no Chemistry Panlc Values to display"); + html.append("There are no Chemistry Alert Values to display"); return; } diff --git a/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest.java b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest.java index bdf812dd0..b6682a733 100644 --- a/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest.java +++ b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest.java @@ -72,6 +72,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.function.Function; import static org.junit.Assert.assertEquals; @@ -557,6 +558,29 @@ public void testArrivalApi() throws Exception Assert.assertEquals(1, demographicsSelect.execute(getApiHelper().getConnection(), getContainerPath()).getRowCount().intValue()); } + @Test + public void testSubmitButtonsDisabledDuringValidation() throws Exception + { + List allIds = createTemporaryValidationAnimals(30); + + try + { + log("Bulk adding animals in treatment orders for temporary test animals"); + _helper.goToTaskForm("Medications/Diet", false); + Ext4GridRef treatmentGrid = _helper.getExt4GridForFormSection("Medication/Treatment Orders"); + addBatchIdsToGrid(treatmentGrid, allIds); + + assertActionsDisabledDuringValidation(); + + treatmentGrid.waitForRowCount(allIds.size()); + _helper.discardForm(); + } + finally + { + deleteTemporaryValidationAnimals(allIds); + } + } + @Test public void testCustomActions() throws Exception { @@ -1103,8 +1127,9 @@ public void testExamEntry() throws Exception Assert.assertEquals(remark, bloodGrid.getFieldValue(3, "remark")); Assert.assertEquals(remark, bloodGrid.getFieldValue(4, "remark")); - waitAndClickAndWait(_helper.getDataEntryButton("Save & Close")); - waitForElement(Locator.tagWithText("a", "Enter New Data")); + waitForDataEntryButtonEnabled("Save & Close", WAIT_FOR_PAGE * 2); + waitAndClick(_helper.getDataEntryButton("Save & Close")); + waitForElement(Locator.tagWithText("a", "Enter New Data"), WAIT_FOR_PAGE * 2); } @Test @@ -1488,6 +1513,9 @@ public void testPathology() //test SNOMED codes _ext4Helper.clickExt4Tab("Histologic Findings"); Ext4GridRef histologyGrid = _helper.getExt4GridForFormSection("Histologic Findings"); + // The custom "Add Record" handler returns early until the tab's grid store finishes loading. + waitFor(() -> (Boolean)histologyGrid.getFnEval("return !!this.store && (!this.store.hasLoaded || this.store.hasLoaded());"), + "Histologic Findings grid store did not finish loading", WAIT_FOR_JAVASCRIPT); _helper.addRecordToGrid(histologyGrid, "Add Record"); scrollIntoView(histologyGrid.getCell(1,7), true); waitAndClick(histologyGrid.getCell(1, 7)); @@ -1566,7 +1594,9 @@ public void testPathology() waitForElementToDisappear(deathWindow, 20000); //saving can take longer than default 10 seconds waitForElementToDisappear(Locator.tagContainingText("div", "Saving Changes...").notHidden()); - waitAndClickAndWait(_helper.getDataEntryButton("Save & Close")); + waitForDataEntryButtonEnabled("Save & Close", WAIT_FOR_PAGE * 2); + waitAndClick(_helper.getDataEntryButton("Save & Close")); + waitForElement(Locator.tagWithText("a", "Enter New Data"), WAIT_FOR_PAGE * 2); //make new necropsy, copy from previous _helper.goToTaskForm("Necropsy", false); @@ -1971,6 +2001,107 @@ private void setNecropsyFormElement(String id, String value) assertEquals(value, getFormElement(loc)); } + private void addBatchIdsToGrid(Ext4GridRef grid, List ids) + { + grid.clickTbarButton("Add Batch"); + waitForElement(Ext4Helper.Locators.window("Choose Animals")); + Ext4FieldRef.getForLabel(this, "Id(s)").setValue(StringUtils.join(ids, ";")); + + waitAndClick(Ext4Helper.Locators.window("Choose Animals").append(Ext4Helper.Locators.ext4Button("Submit"))); + grid.waitForRowCount(ids.size()); + } + + private void assertActionsDisabledDuringValidation() + { + List buttonTexts = Arrays.asList("Save Draft", "Save & Close", "Submit For Review", "Submit Final"); + List menuItemTexts = Arrays.asList("Submit And Reload", "Force Submit"); + Locator.XPathLocator validationIndicator = Locator.tagContainingText("span", "Validating...").notHidden(); + waitFor(() -> !validationIndicator.findElements(getDriver()).isEmpty(), + "Validation indicator never appeared", WAIT_FOR_PAGE); + + for (String buttonText : buttonTexts) + { + List buttons = _ext4Helper.componentQuery("button[text='" + buttonText + "']", Ext4CmpRef.class); + if (!buttons.isEmpty()) + { + waitFor(() -> Boolean.TRUE.equals(buttons.get(0).getEval("isDisabled() == arguments[0]", true)), + buttonText + " did not become disabled during validation", WAIT_FOR_PAGE); + } + } + + waitAndClick(_helper.getDataEntryButton("More Actions")); + waitForElement(Ext4Helper.Locators.menu().notHidden()); + for (String menuItemText : menuItemTexts) + { + waitForElement(Ext4Helper.Locators.menuItemDisabled(menuItemText).notHidden()); + } + waitAndClick(_helper.getDataEntryButton("More Actions")); + waitForElementToDisappear(Ext4Helper.Locators.menu().notHidden()); + + waitFor(() -> validationIndicator.findElements(getDriver()).isEmpty(), + "Validation indicator did not disappear", WAIT_FOR_PAGE * 2); + waitForText(WAIT_FOR_PAGE * 2, "WARN"); + waitForText(WAIT_FOR_PAGE * 2, "ERROR"); + } + + private List createTemporaryValidationAnimals(int count) throws Exception + { + String seed = Long.toString(System.currentTimeMillis()); + seed = seed.substring(Math.max(0, seed.length() - 6)); + + String[] species = {"Rhesus", "Cynomolgus", "Marmoset"}; + String[] fields = {"Id", "Species", "Birth", "Gender", "date", "calculated_status", "objectid"}; + Object[][] data = new Object[count][]; + List ids = new ArrayList<>(); + + for (int i = 0; i < count; i++) + { + String id = "VAL" + seed + String.format("%02d", i + 1); + ids.add(id); + data[i] = new Object[]{ + id, + species[i % species.length], + new Date().toString(), + i % 2 == 0 ? getMale() : getFemale(), + new Date(), + "Alive", + UUID.randomUUID().toString() + }; + } + + getApiHelper().deleteAllRecords("study", "demographics", new Filter("Id", StringUtils.join(ids, ";"), Filter.Operator.IN)); + getApiHelper().doSaveRows(DATA_ADMIN.getEmail(), + getApiHelper().prepareInsertCommand("study", "demographics", "lsid", fields, data), + getExtraContext()); + cacheIds(ids); + + return ids; + } + + private void deleteTemporaryValidationAnimals(List ids) throws Exception + { + if (ids.isEmpty()) + { + return; + } + + getApiHelper().deleteAllRecords("study", "demographics", new Filter("Id", StringUtils.join(ids, ";"), Filter.Operator.IN)); + } + + private void waitForDataEntryButtonEnabled(String buttonText, int timeout) + { + waitFor(() -> { + List buttons = _ext4Helper.componentQuery("button[text='" + buttonText + "']", Ext4CmpRef.class); + if (buttons.isEmpty()) + { + return false; + } + + return Boolean.TRUE.equals(buttons.get(0).getEval("isDisabled() == arguments[0]", false)); + }, + "Button did not become enabled: " + buttonText, timeout); + } + @Override protected String getAnimalHistoryPath() { diff --git a/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest2.java b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest2.java index ee9e2602f..7b0c7fffb 100644 --- a/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest2.java +++ b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_EHRTest2.java @@ -1449,6 +1449,145 @@ public void gridErrorsTest() //TODO: make sure fields turn red as expected } + /** + * Verifies the clinremarks Id field read-only + tooltip behavior wired in via the + * `CaseMgmt` metadata source (onprc_ehr/model/sources/CaseMgmt.js). The source is + * registered against both ClinicalReportFormType and BehaviorExamFormType, so this + * test exercises both forms through a shared helper. + */ + @Test + public void testClinremarksIdReadOnlyOnCaseCreated() throws Exception + { + goToEHRFolder(); + + // Verify case open and open & immediately close. Test Exams/Cases and BSU Exam forms. + verifyClinremarksIdLockOnCaseCreated("Exams/Cases", "Clinical", "Open Case", CaseEntryPath.MANAGE_CASES_LINK); + verifyClinremarksIdLockOnCaseCreated("BSU Exam", "Behavior", "Open & Immediately Close", CaseEntryPath.MANAGE_CASES_LINK); + + // Top and bottom buttons + verifyClinremarksIdLockOnCaseCreated("Exams/Cases", "Clinical", "Open Case", CaseEntryPath.OPEN_MANAGE_TOP); + verifyClinremarksIdLockOnCaseCreated("BSU Exam", "Behavior", "Open Case", CaseEntryPath.OPEN_MANAGE_BOTTOM); + } + + /** How the test reaches the "create new case" dialog from the data entry form. */ + private enum CaseEntryPath + { + /** [Manage Cases] link → "Open Case" split button → "Open <category> Case" menu item. */ + MANAGE_CASES_LINK, + /** "Open/Manage <category> Case" button in the Instructions panel at the top of the form. */ + OPEN_MANAGE_TOP, + /** "Open/Manage <category> Case" button in the form's docked footer toolbar. */ + OPEN_MANAGE_BOTTOM + } + + private void verifyClinremarksIdLockOnCaseCreated(String formLinkLabel, String caseCategory, String openButtonLabel, CaseEntryPath entryPath) + { + final String expectedTooltip = "Refresh the form to enter data for a different animal."; + + log("Verifying clinremarks Id read-only on casecreated for form: " + formLinkLabel + " via '" + openButtonLabel + "' (entry: " + entryPath + ")"); + _helper.goToTaskForm(formLinkLabel, false); + _ext4Helper.clickExt4Tab("SOAP"); + + Ext4FieldRef idField = _helper.getExt4FieldForFormSection("SOAP", "Id"); + Assert.assertNotNull("Could not locate Id field in SOAP section of form: " + formLinkLabel, idField); + + idField.setValue(SUBJECTS[0]); + + // Wait for the AnimalDetailsPanel to display the Id + Ext4FieldRef detailsId = _ext4Helper.queryOne("displayfield[name=animalId]", Ext4FieldRef.class); + Assert.assertNotNull("AnimalDetailsPanel not rendered (form: " + formLinkLabel + ")", detailsId); + waitFor(() -> SUBJECTS[0].equals(String.valueOf(detailsId.getValue())), + "AnimalDetailsPanel did not display Id " + SUBJECTS[0] + " (form: " + formLinkLabel + ")", + WAIT_FOR_JAVASCRIPT); + + Assert.assertNotEquals("Id field should be editable before any case is created (form: " + formLinkLabel + ")", + Boolean.TRUE, idField.getEval("readOnly")); + Object preQtip = idField.getEval("getEl().dom.getAttribute('data-qtip')"); + assertEquals("Id field should have no tooltip before any case is created (form: " + formLinkLabel + ")", + "", preQtip == null ? "" : preQtip.toString()); + + // Open a real case from the data entry form so ManageCasesPanel fires the real casecreated event + createCaseFromForm(SUBJECTS[0], caseCategory, openButtonLabel, entryPath); + + waitFor(() -> Boolean.TRUE.equals(idField.getEval("readOnly")), + "Id field did not become read-only after case was opened (form: " + formLinkLabel + ")", + WAIT_FOR_JAVASCRIPT); + Object postQtip = idField.getEval("getEl().dom.getAttribute('data-qtip')"); + assertEquals("Id field should carry the lock tooltip after case is opened (form: " + formLinkLabel + ")", + expectedTooltip, postQtip == null ? "" : postQtip.toString()); + + _helper.discardForm(); + } + + /** + * Opens a case for {@code animalId} from the data entry form via the given {@code entryPath}, then + * closes the resulting Manage Cases window. {@code openButtonLabel} is one of the OpenCaseWindow submit + * buttons: "Open Case" (just open) or "Open & Immediately Close" (open and immediately close permanently). + */ + private void createCaseFromForm(String animalId, String caseCategory, String openButtonLabel, CaseEntryPath entryPath) + { + Locator.XPathLocator manageCasesWindow = Ext4Helper.Locators.window("Manage Cases: " + animalId); + triggerCreateCaseDialog(manageCasesWindow, caseCategory, entryPath); + + // ManageCasesPanel asks "Open New" vs "Edit Existing" first if an active case of this category + // already exists for the animal (ManageCasesPanel.js: showCreateWindow). Dismiss with "Open New". + Locator.XPathLocator existingCaseDialog = Ext4Helper.Locators.window("Open Case"); + if (Boolean.TRUE.equals(waitFor(() -> isElementPresent(existingCaseDialog), 2000))) + { + waitAndClick(existingCaseDialog.append(Ext4Helper.Locators.ext4ButtonEnabled("Open New"))); + waitForElementToDisappear(existingCaseDialog); + } + + Locator.XPathLocator openCaseWindow = Ext4Helper.Locators.window("Open Case: " + animalId); + waitForElement(openCaseWindow); + + if ("Clinical".equals(caseCategory)) + { + Ext4ComboRef vetField = Ext4ComboRef.getForLabel(this, "Assigned Vet"); + vetField.waitForStoreLoad(); + // Pick whichever vet the store has rather than hardcoding a display name; the test only needs the field populated + vetField.eval("setValue(arguments[0])", vetField.getFnEval("return this.store.getAt(0).get(this.valueField)")); + Ext4FieldRef.getForLabel(this, "Problem").setValue("Behavioral"); + } + else if ("Behavior".equals(caseCategory)) + { + Ext4FieldRef.getForLabel(this, "Subcategory").setValue("Alopecia"); + } + + waitAndClick(openCaseWindow.append(Ext4Helper.Locators.ext4ButtonEnabled(openButtonLabel))); + if ("Open & Immediately Close".equals(openButtonLabel)) + { + waitAndClick(Ext4Helper.Locators.menuItem("Close Permanently").notHidden()); + } + waitForElementToDisappear(openCaseWindow); + + waitAndClick(manageCasesWindow.append(Ext4Helper.Locators.ext4ButtonEnabled("Close"))); + waitForElementToDisappear(manageCasesWindow); + } + + private void triggerCreateCaseDialog(Locator.XPathLocator manageCasesWindow, String caseCategory, CaseEntryPath entryPath) + { + switch (entryPath) + { + case MANAGE_CASES_LINK -> { + waitAndClick(Locator.linkWithText("[Manage Cases]")); + waitForElement(manageCasesWindow); + waitAndClick(manageCasesWindow.append(Ext4Helper.Locators.ext4ButtonEnabled("Open Case"))); + waitAndClick(Ext4Helper.Locators.menuItem("Open " + caseCategory + " Case").notHidden()); + } + case OPEN_MANAGE_TOP -> { + Locator.XPathLocator instructionsPanel = Locator.tagWithClass("div", "x4-panel").withChild( + Locator.tagWithClass("div", "x4-panel-header").withDescendant( + Locator.tagWithClass("span", "x4-panel-header-text").withText("Instructions"))); + waitAndClick(instructionsPanel.append(Ext4Helper.Locators.ext4ButtonEnabled("Open/Manage " + caseCategory + " Case"))); + } + case OPEN_MANAGE_BOTTOM -> { + Locator.XPathLocator footerToolbar = Locator.tagWithClass("div", "x4-toolbar-footer"); + waitAndClick(footerToolbar.append(Ext4Helper.Locators.ext4ButtonEnabled("Open/Manage " + caseCategory + " Case"))); + } + } + } + @Override protected String getAnimalHistoryPath() { diff --git a/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_RestrictedIssueTest.java b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_RestrictedIssueTest.java index 7689b7356..05a537821 100644 --- a/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_RestrictedIssueTest.java +++ b/onprc_ehr/test/src/org/labkey/test/tests/onprc_ehr/ONPRC_RestrictedIssueTest.java @@ -293,13 +293,16 @@ public void restrictedIssueSearchTest() // verify that we can return links even if the user doesn't have permission to view a restricted issue Assert.assertTrue("Number of search results not expected", resultsPage.getResults().size() == 2); - // verify assigned to users will see both results but shouldn't be able to see details of issues not assigned to them + // verify assigned to users will see both results but shouldn't be able to see details of issues not assigned to them, + // also verify that there is a warning rendered if a search result is restricted impersonate(USER1.getEmail()); + assertTextPresent("Restricted Issue: You do not have access. Contact your administrator for access."); verifyIssueAccess(ISSUE_1, true); verifyIssueAccess(ISSUE_2, false); stopImpersonating(false); impersonate(USER2.getEmail()); + assertTextPresent("Restricted Issue: You do not have access. Contact your administrator for access."); verifyIssueAccess(ISSUE_1, false); verifyIssueAccess(ISSUE_2, true); stopImpersonating();