From b234053ced1f96a024afc600c6364dfcf91c341d Mon Sep 17 00:00:00 2001 From: Lakshmi Kolli <69940873+kollil@users.noreply.github.com> Date: Mon, 10 Aug 2026 10:08:27 -0700 Subject: [PATCH 1/2] 26.3 New Diet Enddate exclusion (#1838) Added another Diet snomed to bypass the enddate validation. E-YYY85 - 5000 Chow --- .../queries/onprc_ehr/MedsEndDateAlert.sql | 16 +++--- .../scripts/onprc_ehr/onprc_triggers.js | 7 ++- .../notification/AdminNotifications.java | 52 ++++++++++--------- 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql b/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql index a6dc2db26..83d52ee3d 100644 --- a/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql +++ b/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql @@ -9,14 +9,17 @@ Added Diet to the list by Kollil on 5/14/25. Refer to tkt #12506 5. E-X1380 - Diet Daily (Non-standard), 5LOP (TAD) + + Added Diet to the list by Kollil on 8/5/2026. Refer to tkt #15123 +6. E-YYY85 - Diet, 5000 Chow */ SELECT Id, - date, + CAST(date AS DATE) AS date, enddate, - frequency, + frequency.meaning as frequency, treatmenttimes, - project, + project.displayname as project, code, volumewithunits, concentrationwithunits, @@ -25,10 +28,11 @@ SELECT performedby, remark, reason, - modifiedby, - modified, + modifiedby.displayname as modifiedby, + CAST(modified AS DATE) AS modified, category, + qcstate.label as qcstate, taskid.rowid as TaskId FROM study.treatment_order -WHERE code NOT IN ('E-85760', 'E-Y7735', 'E-X0500', 'E-Y9750', 'E-X1380') +WHERE code NOT IN ('E-85760', 'E-Y7735', 'E-X0500', 'E-Y9750', 'E-X1380', 'E-YYY85') AND enddate is null \ No newline at end of file diff --git a/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js b/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js index 30a298a30..51e4b53e8 100644 --- a/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js +++ b/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js @@ -1064,12 +1064,15 @@ exports.init = function(EHR){ Added Diet to the list by Kollil on 5/14/25. Refer to tkt #12506 5. E-X1380 - Diet Daily (Non-standard), 5LOP (TAD) + + Added Diet to the list by Kollil on 8/5/2026. Refer to tkt #15123 + 6. E-YYY85 - Diet, 5000 Chow */ + if (row.code != 'E-85760' && row.code != 'E-Y7735' && row.code != 'E-X0500' && - row.code != 'E-Y9750' && row.code != 'E-X1380' && !row.enddate) { + row.code != 'E-Y9750' && row.code != 'E-X1380' && row.code != 'E-YYY85' && !row.enddate) { EHR.Server.Utils.addError(scriptErrors, 'enddate', 'Must enter enddate', 'WARN'); } - //Added by Kollil, 9/15/25 /* MPA validation, as per ticket #9669 Add validation code to ensure that MPA is ordered for the correct day: diff --git a/onprc_ehr/src/org/labkey/onprc_ehr/notification/AdminNotifications.java b/onprc_ehr/src/org/labkey/onprc_ehr/notification/AdminNotifications.java index d9fc7fd8e..534ac5cce 100644 --- a/onprc_ehr/src/org/labkey/onprc_ehr/notification/AdminNotifications.java +++ b/onprc_ehr/src/org/labkey/onprc_ehr/notification/AdminNotifications.java @@ -106,13 +106,14 @@ private void MedsEndDateAlert(Container c, User u, final StringBuilder msg, fina "
2. E-Y7735 (Diet - Weekly Multivitamin)" + "
3. E-X0500 (Diet, L-Phyto (Low-phytoestrogen)) " + "
4. E-Y9750 (Diet, 5047 High Protein, Jumbo) " + - "
5. E-X1380 (Diet Daily (Non-standard), 5LOP (TAD))
"); + "
5. E-X1380 (Diet Daily (Non-standard), 5LOP (TAD)) " + + "
6. E-YYY85 (5000 Chow)
"); } else if (count > 0) { //Display the report link on the notification page - msg.append("
" + count + " treatment order(s) found with missing end dates

"); - msg.append("

Click here to view the treatments

\n"); + msg.append("
" + count + " treatment order(s) found with missing end dates. "); + msg.append("Click here to view the Medications/Diets in a grid view\n"); msg.append("
"); //Display the report in the email @@ -134,40 +135,41 @@ else if (count > 0) columns.add(FieldKey.fromString("modifiedby")); columns.add(FieldKey.fromString("modified")); columns.add(FieldKey.fromString("category")); + columns.add(FieldKey.fromString("qcstate")); columns.add(FieldKey.fromString("taskid")); final Map colMap = QueryService.get().getColumns(ti, columns); TableSelector ts2 = new TableSelector(ti, colMap.values(), null, new Sort("date")); // Table header - msg.append(""); - msg.append(""); - msg.append("
"); + + msg.append("
"); msg.append(""); - msg.append(""); + msg.append(""); ts2.forEach(object -> { Results rs = new ResultsImpl(object, colMap); String url = getParticipantURL(c, rs.getString("Id")); - msg.append("\n"); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); - msg.append(""); + msg.append("\n"); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append(""); + msg.append("\n"); msg.append(""); }); msg.append("
Id Begin Date End Date Frequency Times Charge To Treatment Volume Concentration Amount Route Ordered By Remark Reason Modified By Modified Date Category Task Id
Id Begin Date End Date Frequency Times Charge To Treatment Volume Concentration Amount Route Ordered By Remark Reason Modified By Modified Date Category QCState Task Id
" + PageFlowUtil.filter(rs.getString("Id")) + "" + PageFlowUtil.filter(rs.getString("date")) + "" + PageFlowUtil.filter(rs.getString("enddate")) + "" + PageFlowUtil.filter(rs.getString("frequency")) + "" + PageFlowUtil.filter(rs.getString("treatmentTimes")) + "" + PageFlowUtil.filter(rs.getString("project")) + "" + PageFlowUtil.filter(rs.getString("code")) + "" + PageFlowUtil.filter(rs.getString("volumewithunits")) + "" + PageFlowUtil.filter(rs.getString("concentrationwithunits")) + "" + PageFlowUtil.filter(rs.getString("amountwithunits")) + "" + PageFlowUtil.filter(rs.getString("route")) + "" + PageFlowUtil.filter(rs.getString("performedby")) + "" + PageFlowUtil.filter(rs.getString("remark")) + "" + PageFlowUtil.filter(rs.getString("reason")) + "" + PageFlowUtil.filter(rs.getString("modifiedby")) + "" + PageFlowUtil.filter(rs.getString("modified")) + "" + PageFlowUtil.filter(rs.getString("category")) + "" + PageFlowUtil.filter(rs.getString("taskid")) + " " + PageFlowUtil.filter(rs.getString("Id")) + " " + PageFlowUtil.filter(rs.getString("date")) + "" + PageFlowUtil.filter(rs.getString("enddate")) + "" + PageFlowUtil.filter(rs.getString("frequency")) + "" + PageFlowUtil.filter(rs.getString("treatmentTimes")) + "" + PageFlowUtil.filter(rs.getString("project")) + "" + PageFlowUtil.filter(rs.getString("code")) + "" + PageFlowUtil.filter(rs.getString("volumewithunits")) + "" + PageFlowUtil.filter(rs.getString("concentrationwithunits")) + "" + PageFlowUtil.filter(rs.getString("amountwithunits")) + "" + PageFlowUtil.filter(rs.getString("route")) + "" + PageFlowUtil.filter(rs.getString("performedby")) + "" + PageFlowUtil.filter(rs.getString("remark")) + "" + PageFlowUtil.filter(rs.getString("reason")) + "" + PageFlowUtil.filter(rs.getString("modifiedby")) + "" + PageFlowUtil.filter(rs.getString("modified")) + "" + PageFlowUtil.filter(rs.getString("category")) + "" + PageFlowUtil.filter(rs.getString("qcstate")) + "" + PageFlowUtil.filter(rs.getString("taskid")) + "
"); From 5dcd65ba66d65e57c9766be28f9468748e4cb8f0 Mon Sep 17 00:00:00 2001 From: Brent Logan Date: Tue, 11 Aug 2026 14:50:42 -0700 Subject: [PATCH 2/2] Remove two tables from audit log archiving (#1762) #### Rationale The ArchiveAuditLogs ETL archives audit logs older than xyz. Unfortunately, this removes historic permissions and login information from PRIMe's back end. This PR prevents the archiving of the two relevant tables. #### Related Pull Requests * #1536 #### Changes * --- .../sqlserver/onprc_ehr-26.002-26.003.sql | 255 ++++++++++++++++++ .../org/labkey/onprc_ehr/ONPRC_EHRModule.java | 2 +- 2 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.002-26.003.sql diff --git a/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.002-26.003.sql b/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.002-26.003.sql new file mode 100644 index 000000000..5159b21df --- /dev/null +++ b/onprc_ehr/resources/schemas/dbscripts/sqlserver/onprc_ehr-26.002-26.003.sql @@ -0,0 +1,255 @@ +SET +QUOTED_IDENTIFIER ON; +GO + +ALTER PROCEDURE +[audit].[ArchiveAuditTables] ( + @RetentionMonths INT OUTPUT +) +AS +BEGIN + SET +NOCOUNT ON; + + -- Declare variables + DECLARE +@SourceDB NVARCHAR(128) = DB_NAME(), + @DestDB NVARCHAR(128) = 'labkey_audit', + @SchemaName NVARCHAR(128) = 'audit'; + + SET +@RetentionMonths = CASE WHEN @RetentionMonths - 6 > 12 THEN @RetentionMonths - 6 ELSE 12 +END; + PRINT +N'Archiving audit logs older than ' + CAST(@RetentionMonths AS NVARCHAR(3)) + N' months old' + + DECLARE +@CutoffDate DATETIME = DATEADD(MONTH, -@RetentionMonths, GETDATE()); + + + -- Validate if source database exists + IF +NOT EXISTS (SELECT 1 FROM sys.databases WHERE NAME = @SourceDB) +BEGIN + RAISERROR +('Source database "%s" does not exist.', 16, 1, @SourceDB); + RETURN; +END + + -- Validate if destination database exists + IF +NOT EXISTS (SELECT 1 FROM sys.databases WHERE NAME = @DestDB) +BEGIN + RAISERROR +('Destination database "%s" does not exist.', 16, 1, @DestDB); + RETURN; +END + + -- Create ArchiveAuditLog table if not exists (useful for testing) + DECLARE +@CreateLogTableSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ''dbo'' AND TABLE_NAME = ''ArchiveAuditLog'') + BEGIN + EXEC(''USE ' + QUOTENAME(@DestDB) + '; + CREATE TABLE dbo.ArchiveAuditLog ( + LogID INT IDENTITY(1,1) NOT NULL, + TableName NVARCHAR(128) NOT NULL, + Operation NVARCHAR(50) NOT NULL, + StartTime DATETIME NOT NULL, + EndTime DATETIME NULL, + Status NVARCHAR(50) NULL, + RecordsProcessed INT NULL, + ErrorMessage NVARCHAR(MAX) NULL, + RetentionMonths INT NULL, + CONSTRAINT PK_ArchiveAuditLog PRIMARY KEY (LogID) + )''); + END'; + +EXEC sp_executesql @CreateLogTableSQL; + + -- Create RetentionMonths column in ArchiveAuditLog table if not exist + DECLARE +@CreateRetentionColumnSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = ''dbo'' + AND TABLE_NAME = ''ArchiveAuditLog'' + AND COLUMN_NAME = ''RetentionMonths'') + BEGIN + EXEC(''USE ' + QUOTENAME(@DestDB) + '; + ALTER TABLE dbo.ArchiveAuditLog + ADD RetentionMonths INT NULL + ''); + END'; + +EXEC sp_executesql @CreateRetentionColumnSQL; + + -- Validate if source schema exists + DECLARE +@SourceSchemaCheck NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@SourceDB) + '.sys.schemas WHERE name = ''' + @SchemaName + ''') + BEGIN + RAISERROR(''Source schema "%s" does not exist'', 16, 1, ''' + @SchemaName + '''); + END'; + +EXEC sp_executesql @SourceSchemaCheck; + + -- Create destination schema if not exists + DECLARE +@CreateDestSchemaSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.sys.schemas WHERE name = ''' + @SchemaName + ''') + BEGIN + EXEC ' + QUOTENAME(@DestDB) + '.sys.sp_executesql N''CREATE SCHEMA ' + QUOTENAME(@SchemaName) + '''; + END'; + +EXEC sp_executesql @CreateDestSchemaSQL; + + -- Get list of tables to process +CREATE TABLE #TableList +( + TableName NVARCHAR(128) +); + +DECLARE +@GetTablesSQL NVARCHAR(MAX) = ' + INSERT INTO #TableList + SELECT TABLE_NAME + FROM ' + QUOTENAME(@SourceDB) + '.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ''' + @SchemaName + ''' + AND TABLE_NAME NOT IN (''c3d330_userauditdomain'', ''c3d317_groupauditdomain'')'; + +EXEC sp_executesql @GetTablesSQL; + + DECLARE +@CurrentTable NVARCHAR(128); + DECLARE +TableCursor CURSOR LOCAL FAST_FORWARD FOR +SELECT TableName FROM #TableList; + +OPEN TableCursor; +FETCH NEXT FROM TableCursor INTO @CurrentTable; + +WHILE +@@FETCH_STATUS = 0 +BEGIN + DECLARE +@LogID INT; + + -- Log the start of archiving for current table + DECLARE +@InsertLogSQL NVARCHAR(MAX) = ' + USE ' + QUOTENAME(@DestDB) + '; + INSERT INTO dbo.ArchiveAuditLog + (TableName, Operation, StartTime, Status, RetentionMonths) + VALUES (''' + @CurrentTable + ''', ''Archive'', GETDATE(), ''Started'', ' + CAST(@RetentionMonths AS NVARCHAR(10)) + '); + SELECT @LogIDOUT = SCOPE_IDENTITY();'; + +EXEC sp_executesql @InsertLogSQL, N'@LogIDOUT INT OUTPUT', @LogIDOUT = @LogID OUTPUT; + +BEGIN TRY + DECLARE +@FullSourceTable NVARCHAR(512) = QUOTENAME(@SourceDB) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@CurrentTable), + @FullDestTable NVARCHAR(512) = QUOTENAME(@DestDB) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@CurrentTable), + @ColumnList NVARCHAR(MAX) = ''; + + -- Create destination table if it doesn't exist + DECLARE +@CheckTableSQL NVARCHAR(MAX) = ' + IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(@DestDB) + '.INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ''' + @SchemaName + ''' + AND TABLE_NAME = ''' + @CurrentTable + ''') + BEGIN + SELECT * INTO ' + @FullDestTable + ' + FROM ' + @FullSourceTable + ' + WHERE 1 = 0; + END'; + +EXEC sp_executesql @CheckTableSQL; + + -- Get column list (excluding identity columns) +CREATE TABLE #Columns +( + ColumnName NVARCHAR(128), + IsIdentity BIT +); + +DECLARE +@GetColumnsSQL NVARCHAR(MAX) = ' + INSERT INTO #Columns + SELECT c.name AS ColumnName, + COLUMNPROPERTY(OBJECT_ID(''' + @FullSourceTable + '''), c.name, ''IsIdentity'') AS IsIdentity + FROM ' + QUOTENAME(@SourceDB) + '.sys.columns c + JOIN ' + QUOTENAME(@SourceDB) + '.sys.tables t ON c.object_id = t.object_id + JOIN ' + QUOTENAME(@SourceDB) + '.sys.schemas s ON t.schema_id = s.schema_id + WHERE s.name = ''' + @SchemaName + ''' + AND t.name = ''' + @CurrentTable + ''''; + +EXEC sp_executesql @GetColumnsSQL; + +SELECT @ColumnList = STRING_AGG(QUOTENAME(ColumnName), ', ') +FROM #Columns +WHERE IsIdentity = 0; + +DROP TABLE #Columns; + +-- Archive data +BEGIN +TRANSACTION; + + DECLARE +@ArchiveSQL NVARCHAR(MAX) = ' + INSERT INTO ' + @FullDestTable + ' (' + @ColumnList + ') + SELECT ' + @ColumnList + ' + FROM ' + @FullSourceTable + ' + WHERE Created < @CutoffDate; + + DECLARE @RecordsInserted INT = @@ROWCOUNT; + + DELETE FROM ' + @FullSourceTable + ' + WHERE Created < @CutoffDate; + + DECLARE @RecordsDeleted INT = @@ROWCOUNT; + + UPDATE ' + QUOTENAME(@DestDB) + '.dbo.ArchiveAuditLog + SET RecordsProcessed = @RecordsInserted, + EndTime = GETDATE(), + Status = ''Success'' + WHERE LogID = @LogID;'; + +EXEC sp_executesql @ArchiveSQL, + N'@CutoffDate DATETIME, @LogID INT', + @CutoffDate = @CutoffDate, + @LogID = @LogID; + +COMMIT TRANSACTION; +END TRY +BEGIN CATCH +IF @@TRANCOUNT > 0 + ROLLBACK TRANSACTION; + + DECLARE +@ErrorMessage NVARCHAR(4000) = 'Error archiving ' + @CurrentTable + ': ' + ERROR_MESSAGE(); + + DECLARE +@UpdateLogSQL NVARCHAR(MAX) = ' + UPDATE ' + QUOTENAME(@DestDB) + '.dbo.ArchiveAuditLog + SET EndTime = GETDATE(), + Status = ''Error'', + ErrorMessage = @ErrorMessage + WHERE LogID = ' + CAST(@LogID AS NVARCHAR(10)); + +EXEC sp_executesql @UpdateLogSQL, N'@ErrorMessage NVARCHAR(4000)', @ErrorMessage = @ErrorMessage; + + PRINT +@ErrorMessage; +END CATCH + +FETCH NEXT FROM TableCursor INTO @CurrentTable; +END + +CLOSE TableCursor; +DEALLOCATE +TableCursor; + +DROP TABLE #TableList; +END \ No newline at end of file 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 5d014e1b8..378583262 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 26.002; + return 26.003; } @Override