From 51597a4f4b1dc96878eb9fb66cd8c7abcaa1772c Mon Sep 17 00:00:00 2001 From: Adam Weber Date: Sun, 17 May 2026 19:58:56 -0700 Subject: [PATCH 1/3] Fix RTR bug #3: rewrite morbidity report user comment join via NRT staging CSV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The step "Generating ##SAS_morb_Rpt_User_Comment" joined the Order observation to itself via root.morb_rpt_uid = obs.observation_uid and then filtered obs.obs_domain_cd_st_1 IN ('C_Order','C_Result'), which is impossible because the Order row's obs_domain_cd_st_1 is 'Order'. The temp table was therefore always empty and MORB_RPT_USER_COMMENT never populated. Replaced the broken self-join with a staging-side walk: the upstream NRT row #nrt_morbidity_observation already carries followup_observation_uid as a CSV of the Order's children (mixed C_Order / C_Result / Result), so this step expands that CSV via CROSS APPLY string_split and joins to #morb_obs_reference filtered to obs_domain_cd_st_1 = 'C_Result' to pull the user-comment row. Stays entirely within RDB_MODERN staging — no cross-DB read of nbs_odse.dbo.act_relationship, consistent with the sp_nrt_*_postprocessing layer's "NRT-only" convention (cross-DB ODSE joins live exclusively in sp_*_event SPs). Verified locally: applied the routine, truncated MORB_RPT_USER_COMMENT, ran sp_d_morbidity_report_postprocessing for the seeded morb (uid 20080010), confirmed 1 row populated with the seeded user-comment text. job_flow_log shows step 19 (build ##SAS_morb_Rpt_User_Comment) and step 27 (Insert into morb_Rpt_User_Comment) both at row_count=1 with no errors. --- ...rt_morbidity_report_postprocessing-001.sql | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql b/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql index 61e75a131..6db2cdac6 100644 --- a/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql +++ b/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql @@ -799,21 +799,33 @@ BEGIN END;') + -- Find the morb Order's C_Result child (user-comment row) via the + -- staging-side projection of the act_relationship graph. The upstream + -- NRT row #nrt_morbidity_observation already carries + -- followup_observation_uid as a CSV of the Order's children + -- (mixed C_Order / C_Result / Result), so this step stays in + -- RDB_MODERN staging — no cross-DB read of nbs_odse.dbo.act_relationship. + -- The C_Result domain code is what distinguishes the user-comment row + -- from lab-result and C_Order siblings. SET @sql = N' SELECT root.morb_Rpt_Key, root.morb_rpt_uid, - obs.activity_to_time AS user_comments_dt, - obs.add_user_id AS user_comments_by, + cr.activity_to_time AS user_comments_dt, + cr.add_user_id AS user_comments_by, REPLACE(REPLACE(ovt.ovt_value_txt, CHAR(13) + CHAR(10),'' ''), CHAR(10), '' '') AS external_morb_rpt_comments, root.record_status_cd INTO '+@SAS_morb_Rpt_User_Comment+' FROM '+@tmp_Morbidity_Report+' as root - INNER JOIN #morb_obs_reference AS obs ON root.morb_rpt_uid = obs.observation_uid - INNER JOIN #updated_morb_observation_list AS ls ON ls.observation_uid = obs.observation_uid - INNER JOIN #tmp_nrt_observation_txt AS ovt ON ovt.observation_uid = obs.observation_uid + INNER JOIN #nrt_morbidity_observation AS nmo + ON nmo.observation_uid = root.morb_rpt_uid + CROSS APPLY string_split(rtrim(ltrim(nmo.followup_observation_uid)), '','') AS followupObs + INNER JOIN #morb_obs_reference AS cr + ON cr.observation_uid = TRY_CAST(followupObs.value AS bigint) + AND cr.obs_domain_cd_st_1 = ''C_Result'' + INNER JOIN #tmp_nrt_observation_txt AS ovt + ON ovt.observation_uid = cr.observation_uid WHERE - ovt.ovt_value_txt IS NOT NULL - AND obs.obs_domain_cd_st_1 IN (''C_Order'', ''C_Result'');'; + ovt.ovt_value_txt IS NOT NULL;'; IF @debug = 'true' print @sql; EXEC sp_executesql @sql; From 0ffe3151696c17fac8f92e6ee94a195a50bdd1ee Mon Sep 17 00:00:00 2001 From: Adam Weber Date: Tue, 2 Jun 2026 04:51:37 +0000 Subject: [PATCH 2/3] Add unit test covering MORB_RPT_USER_COMMENT population (RTR bug #3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regression coverage for the fix in 51597a4f. Seeds a minimal externally- entered morb report into RDB_MODERN staging — patient, the Order, a C_Order (MorbComment) sibling, and the C_Result (MRB180) user comment with its text — then EXECs sp_d_morbidity_report_postprocessing and asserts one row lands in MORB_RPT_USER_COMMENT with the comment text. The fixture mirrors the real UI-entered shape: NBS flattens the Order's descendant observation_uids into nrt_observation.followup_observation_uid as a CSV, and the comment text lives on the C_Result child. The C_Order sibling is included so the test exercises the obs_domain_cd_st_1='C_Result' discriminator. Against the pre-fix self-join the query returns 0 rows; against the fix it returns 1. Runs under DataDrivenUnitTests (test-unit). --- .../morbidityReportUserComment/expected.json | 11 +++ .../unit/morbidityReportUserComment/query.sql | 8 ++ .../unit/morbidityReportUserComment/setup.sql | 96 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/expected.json create mode 100644 reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/query.sql create mode 100644 reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql diff --git a/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/expected.json b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/expected.json new file mode 100644 index 000000000..932135ff9 --- /dev/null +++ b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/expected.json @@ -0,0 +1,11 @@ +{ + "0": [ + { + "MORB_RPT_UID": 92000001, + "EXTERNAL_MORB_RPT_COMMENTS": "comment from a user on an externally created morb report", + "USER_COMMENTS_BY": 10009282, + "USER_COMMENTS_DT": "2026-06-02 00:17:01", + "RECORD_STATUS_CD": "ACTIVE" + } + ] +} diff --git a/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/query.sql b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/query.sql new file mode 100644 index 000000000..84caaa2b2 --- /dev/null +++ b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/query.sql @@ -0,0 +1,8 @@ +SELECT + MORB_RPT_UID, + EXTERNAL_MORB_RPT_COMMENTS, + USER_COMMENTS_BY, + USER_COMMENTS_DT, + RECORD_STATUS_CD +FROM RDB_MODERN.dbo.MORB_RPT_USER_COMMENT +WHERE MORB_RPT_UID = 92000001; diff --git a/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql new file mode 100644 index 000000000..091b2a72e --- /dev/null +++ b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql @@ -0,0 +1,96 @@ +-- ===================================================================== +-- Unit test: sp_d_morbidity_report_postprocessing populates +-- MORB_RPT_USER_COMMENT from an externally-entered morb +-- report's user comment. +-- +-- Regression coverage for RTR bug #3 (PR #837). A user comment on an +-- externally-entered Morbidity Report is stored in NBS as a C_Result +-- child observation (cd = MRB180, "User Report Comment") hung off the +-- morb Order via a C_Order ("MorbComment") wrapper. NBS flattens the +-- Order's descendant observation_uids into nrt_observation. +-- followup_observation_uid as a CSV, so the postprocessing SP walks that +-- CSV and selects the C_Result sibling to populate +-- MORB_RPT_USER_COMMENT.EXTERNAL_MORB_RPT_COMMENTS. +-- +-- The pre-fix SP self-joined the Order to itself and filtered +-- obs_domain_cd_st_1 IN ('C_Order','C_Result') — impossible for the +-- Order row (domain 'Order') — so the table never populated. +-- +-- Shape modelled (minimal, mirrors a real UI-entered comment): +-- Order 92000001 obs_domain 'Order' ctrl_cd_display_form 'MorbReport' +-- |-- followup_observation_uid CSV = '92000002,92000003' +-- C_Order 92000002 obs_domain 'C_Order' ('MorbComment' wrapper) -- excluded +-- C_Result 92000003 obs_domain 'C_Result' (cd 'MRB180') -- the comment +-- |-- nrt_observation_txt: 'comment from a user on an externally created morb report' +-- D_PATIENT 92000010 supplies the non-null PATIENT_KEY the +-- MORBIDITY_REPORT_EVENT insert requires (it runs before the comment insert). +-- ===================================================================== +USE RDB_MODERN; + +DECLARE @order_uid bigint = 92000001; -- Morb Order (externally entered morb report) +DECLARE @corder_uid bigint = 92000002; -- C_Order child (MorbComment wrapper) -- must be EXCLUDED +DECLARE @cresult_uid bigint = 92000003; -- C_Result child (MRB180 user comment) -- the comment row +DECLARE @patient_uid bigint = 92000010; -- patient the morb report is about + +-- ---- idempotent cleanup (test data only) ---- +DELETE FROM RDB_MODERN.dbo.MORBIDITY_REPORT_EVENT WHERE MORB_RPT_KEY IN (SELECT MORB_RPT_KEY FROM RDB_MODERN.dbo.MORBIDITY_REPORT WHERE morb_rpt_uid = @order_uid); +DELETE FROM RDB_MODERN.dbo.MORB_RPT_USER_COMMENT WHERE morb_rpt_uid = @order_uid; +DELETE FROM RDB_MODERN.dbo.MORBIDITY_REPORT WHERE morb_rpt_uid = @order_uid; +DELETE FROM RDB_MODERN.dbo.D_PATIENT WHERE patient_uid = @patient_uid; +DELETE FROM RDB_MODERN.dbo.nrt_observation_txt WHERE observation_uid IN (@order_uid,@corder_uid,@cresult_uid); +DELETE FROM RDB_MODERN.dbo.nrt_observation WHERE observation_uid IN (@order_uid,@corder_uid,@cresult_uid); + +-- ---- patient dimension row (supplies MORBIDITY_REPORT_EVENT.PATIENT_KEY) ---- +INSERT INTO RDB_MODERN.dbo.D_PATIENT (PATIENT_KEY, patient_uid) VALUES (@patient_uid, @patient_uid); + +-- ---- the morb Order (externally entered) ---- +INSERT INTO RDB_MODERN.dbo.nrt_observation + (observation_uid, class_cd, mood_cd, obs_domain_cd_st_1, ctrl_cd_display_form, + cd, cd_desc_txt, local_id, record_status_cd, electronic_ind, shared_ind, + jurisdiction_cd, prog_area_cd, program_jurisdiction_oid, patient_id, + add_user_id, add_user_name, add_time, last_chg_time, + activity_to_time, rpt_to_state_time, followup_observation_uid, + version_ctrl_nbr) +VALUES + (@order_uid, 'OBS', 'EVN', 'Order', 'MorbReport', + '10311', 'Syphilis, primary', 'OBS92000001GA01', 'UNPROCESSED', 'E', 'T', + '130001', 'STD', 1300100015, @patient_uid, + 10009283, 'Person, External', '2026-06-01 23:54:56', '2026-06-02 00:17:35', + '2026-06-01 23:54:56', '2026-06-01 23:54:56', + CAST(@corder_uid AS varchar(20)) + ',' + CAST(@cresult_uid AS varchar(20)), + 2); + +-- ---- the C_Order child (MorbComment wrapper) -- a non-C_Result sibling that must be excluded ---- +INSERT INTO RDB_MODERN.dbo.nrt_observation + (observation_uid, class_cd, mood_cd, obs_domain_cd_st_1, ctrl_cd_display_form, + cd, cd_desc_txt, local_id, record_status_cd, shared_ind, status_cd, + report_observation_uid, program_jurisdiction_oid, + version_ctrl_nbr) +VALUES + (@corder_uid, 'OBS', 'EVN', 'C_Order', 'MorbComment', + 'NI', 'No Information Given', 'OBS92000002GA01', 'ACTIVE', 'T', 'D', + @order_uid, 4, + 1); + +-- ---- the C_Result child (MRB180 user comment) -- the user-comment row ---- +INSERT INTO RDB_MODERN.dbo.nrt_observation + (observation_uid, class_cd, mood_cd, obs_domain_cd_st_1, + cd, cd_desc_txt, cd_system_cd, local_id, record_status_cd, shared_ind, status_cd, + report_observation_uid, program_jurisdiction_oid, + add_user_id, add_user_name, activity_to_time, effective_from_time, rpt_to_state_time, + version_ctrl_nbr) +VALUES + (@cresult_uid, 'OBS', 'EVN', 'C_Result', + 'MRB180', 'User Report Comment', 'NBS', 'OBS92000003GA01', 'ACTIVE', 'T', 'D', + @corder_uid, 4, + 10009282, 'Kent, Ariella', '2026-06-02 00:17:01', '2026-06-02 00:17:01', '2026-06-02 00:17:01', + 1); + +-- ---- the comment text, carried on the C_Result ---- +INSERT INTO RDB_MODERN.dbo.nrt_observation_txt + (observation_uid, ovt_seq, ovt_txt_type_cd, ovt_value_txt) +VALUES + (@cresult_uid, 1, 'N', 'comment from a user on an externally created morb report'); + +-- ---- run the postprocessing SP for this morb report ---- +EXEC RDB_MODERN.dbo.sp_d_morbidity_report_postprocessing @pMorbidityIdList = N'92000001', @debug = 0; From 69546e950e605fa889b153ffc31c99c763f96ad0 Mon Sep 17 00:00:00 2001 From: Adam Weber Date: Wed, 3 Jun 2026 06:38:09 +0000 Subject: [PATCH 3/3] Tidy up SQL comments for RTR bug #3 fix --- ...rt_morbidity_report_postprocessing-001.sql | 13 ++--- .../unit/morbidityReportUserComment/setup.sql | 51 ++++++++----------- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql b/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql index 6db2cdac6..fa7e6b309 100644 --- a/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql +++ b/liquibase-service/src/main/resources/db/005-rdb_modern/routines/016-sp_nrt_morbidity_report_postprocessing-001.sql @@ -799,14 +799,11 @@ BEGIN END;') - -- Find the morb Order's C_Result child (user-comment row) via the - -- staging-side projection of the act_relationship graph. The upstream - -- NRT row #nrt_morbidity_observation already carries - -- followup_observation_uid as a CSV of the Order's children - -- (mixed C_Order / C_Result / Result), so this step stays in - -- RDB_MODERN staging — no cross-DB read of nbs_odse.dbo.act_relationship. - -- The C_Result domain code is what distinguishes the user-comment row - -- from lab-result and C_Order siblings. + -- Grab the morb Order's user-comment row from staging. #nrt_morbidity_observation + -- already carries followup_observation_uid as a CSV of the Order's children, so we + -- split it here rather than reading nbs_odse.dbo.act_relationship across DBs. The + -- comment is the C_Result child, so filter on that domain code to skip the + -- C_Order and lab-result siblings. SET @sql = N' SELECT root.morb_Rpt_Key, root.morb_rpt_uid, diff --git a/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql index 091b2a72e..8605314a3 100644 --- a/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql +++ b/reporting-pipeline-service/src/test/resources/testData/unit/morbidityReportUserComment/setup.sql @@ -1,36 +1,27 @@ --- ===================================================================== --- Unit test: sp_d_morbidity_report_postprocessing populates --- MORB_RPT_USER_COMMENT from an externally-entered morb --- report's user comment. +-- Unit test: sp_d_morbidity_report_postprocessing should populate +-- MORB_RPT_USER_COMMENT from an externally-entered morb report's user comment. -- --- Regression coverage for RTR bug #3 (PR #837). A user comment on an --- externally-entered Morbidity Report is stored in NBS as a C_Result --- child observation (cd = MRB180, "User Report Comment") hung off the --- morb Order via a C_Order ("MorbComment") wrapper. NBS flattens the --- Order's descendant observation_uids into nrt_observation. --- followup_observation_uid as a CSV, so the postprocessing SP walks that --- CSV and selects the C_Result sibling to populate --- MORB_RPT_USER_COMMENT.EXTERNAL_MORB_RPT_COMMENTS. +-- Regression coverage for RTR bug #3 (PR #837). The comment lives in NBS as a +-- C_Result child observation (cd MRB180, "User Report Comment") hung off the morb +-- Order through a C_Order ("MorbComment") wrapper. NBS flattens the Order's child +-- observation_uids into followup_observation_uid as a CSV, so the SP walks that CSV +-- and picks the C_Result sibling. -- --- The pre-fix SP self-joined the Order to itself and filtered --- obs_domain_cd_st_1 IN ('C_Order','C_Result') — impossible for the --- Order row (domain 'Order') — so the table never populated. +-- The old SP self-joined the Order to itself and filtered obs_domain_cd_st_1 IN +-- ('C_Order','C_Result'), which the Order row (domain 'Order') never matches, so +-- the table stayed empty. -- --- Shape modelled (minimal, mirrors a real UI-entered comment): --- Order 92000001 obs_domain 'Order' ctrl_cd_display_form 'MorbReport' --- |-- followup_observation_uid CSV = '92000002,92000003' --- C_Order 92000002 obs_domain 'C_Order' ('MorbComment' wrapper) -- excluded --- C_Result 92000003 obs_domain 'C_Result' (cd 'MRB180') -- the comment --- |-- nrt_observation_txt: 'comment from a user on an externally created morb report' --- D_PATIENT 92000010 supplies the non-null PATIENT_KEY the --- MORBIDITY_REPORT_EVENT insert requires (it runs before the comment insert). --- ===================================================================== +-- Rows set up below: +-- Order 92000001 domain 'Order' followup CSV = '92000002,92000003' +-- C_Order 92000002 domain 'C_Order' ('MorbComment' wrapper, excluded) +-- C_Result 92000003 domain 'C_Result' (cd 'MRB180', carries the comment text) +-- D_PATIENT 92000010 gives MORBIDITY_REPORT_EVENT its non-null PATIENT_KEY. USE RDB_MODERN; -DECLARE @order_uid bigint = 92000001; -- Morb Order (externally entered morb report) -DECLARE @corder_uid bigint = 92000002; -- C_Order child (MorbComment wrapper) -- must be EXCLUDED -DECLARE @cresult_uid bigint = 92000003; -- C_Result child (MRB180 user comment) -- the comment row -DECLARE @patient_uid bigint = 92000010; -- patient the morb report is about +DECLARE @order_uid bigint = 92000001; -- morb Order (externally entered report) +DECLARE @corder_uid bigint = 92000002; -- C_Order wrapper, excluded +DECLARE @cresult_uid bigint = 92000003; -- C_Result, the comment row +DECLARE @patient_uid bigint = 92000010; -- patient the report is about -- ---- idempotent cleanup (test data only) ---- DELETE FROM RDB_MODERN.dbo.MORBIDITY_REPORT_EVENT WHERE MORB_RPT_KEY IN (SELECT MORB_RPT_KEY FROM RDB_MODERN.dbo.MORBIDITY_REPORT WHERE morb_rpt_uid = @order_uid); @@ -60,7 +51,7 @@ VALUES CAST(@corder_uid AS varchar(20)) + ',' + CAST(@cresult_uid AS varchar(20)), 2); --- ---- the C_Order child (MorbComment wrapper) -- a non-C_Result sibling that must be excluded ---- +-- ---- the C_Order child (MorbComment wrapper); a sibling that should be excluded ---- INSERT INTO RDB_MODERN.dbo.nrt_observation (observation_uid, class_cd, mood_cd, obs_domain_cd_st_1, ctrl_cd_display_form, cd, cd_desc_txt, local_id, record_status_cd, shared_ind, status_cd, @@ -72,7 +63,7 @@ VALUES @order_uid, 4, 1); --- ---- the C_Result child (MRB180 user comment) -- the user-comment row ---- +-- ---- the C_Result child (MRB180), the user comment ---- INSERT INTO RDB_MODERN.dbo.nrt_observation (observation_uid, class_cd, mood_cd, obs_domain_cd_st_1, cd, cd_desc_txt, cd_system_cd, local_id, record_status_cd, shared_ind, status_cd,