diff --git a/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql b/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql index 40197ffb0..ee87fc0ce 100644 --- a/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql +++ b/onprc_ehr/resources/queries/onprc_ehr/MedsEndDateAlert.sql @@ -14,14 +14,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, @@ -30,10 +33,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/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/resources/scripts/onprc_ehr/onprc_triggers.js b/onprc_ehr/resources/scripts/onprc_ehr/onprc_triggers.js index c3e564f30..ff62d73b8 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/ONPRC_EHRModule.java b/onprc_ehr/src/org/labkey/onprc_ehr/ONPRC_EHRModule.java index d19499170..eb1347e17 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 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 53da616d9..86c3c7556 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")) + "
");