Skip to content

Commit 68f7b35

Browse files
authored
BFD-4071: Patient Identities filtering out kill credit (#2751)
1 parent 3b27051 commit 68f7b35

16 files changed

Lines changed: 703 additions & 80 deletions

File tree

apps/bfd-pipeline/bfd-pipeline-idr/bfd.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ CREATE TABLE idr.beneficiary_election_period_usage (
138138
PRIMARY KEY(bene_sk, cntrct_pbp_sk, bene_enrlmt_efctv_dt)
139139
);
140140

141+
CREATE TABLE idr.beneficiary_xref (
142+
bene_sk BIGINT NOT NULL,
143+
bene_xref_sk BIGINT NOT NULL,
144+
bene_hicn_num VARCHAR(11) NOT NULL,
145+
bene_kill_cred_cd VARCHAR(1) NOT NULL,
146+
idr_insrt_ts TIMESTAMPTZ NOT NULL,
147+
idr_updt_ts TIMESTAMPTZ NOT NULL,
148+
src_rec_ctre_ts TIMESTAMPTZ NOT NULL,
149+
bfd_created_ts TIMESTAMPTZ NOT NULL,
150+
bfd_updated_ts TIMESTAMPTZ NOT NULL,
151+
PRIMARY KEY(bene_sk, bene_hicn_num, src_rec_ctre_ts)
152+
);
153+
141154
CREATE TABLE idr.contract_pbp_number (
142155
cntrct_pbp_sk BIGINT NOT NULL PRIMARY KEY,
143156
cntrct_drug_plan_ind_cd VARCHAR(1) NOT NULL,
@@ -332,6 +345,7 @@ CREATE TABLE idr.claim_ansi_signature (
332345

333346
CREATE MATERIALIZED VIEW idr.overshare_mbis AS
334347
SELECT bene_mbi_id FROM idr.beneficiary
348+
WHERE bene_xref_efctv_sk != 0
335349
GROUP BY bene_mbi_id
336350
HAVING COUNT(DISTINCT bene_xref_efctv_sk) > 1;
337351

apps/bfd-pipeline/bfd-pipeline-idr/load_synthetic.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
{"csv_name": "SYNTHETIC_BENE.csv", "table": "v2_mdcr_bene"},
99
{"csv_name": "SYNTHETIC_BENE_HSTRY.csv", "table": "v2_mdcr_bene_hstry"},
1010
{"csv_name": "SYNTHETIC_BENE_MBI_ID.csv", "table": "v2_mdcr_bene_mbi_id"},
11+
{"csv_name": "SYNTHETIC_BENE_XREF.csv", "table": "v2_mdcr_bene_xref"},
1112
{"csv_name": "SYNTHETIC_CLM.csv", "table": "v2_mdcr_clm"},
1213
{"csv_name": "SYNTHETIC_CLM_INSTNL.csv", "table": "v2_mdcr_clm_instnl"},
1314
{"csv_name": "SYNTHETIC_CLM_DCMTN.csv", "table": "v2_mdcr_clm_dcmtn"},

apps/bfd-pipeline/bfd-pipeline-idr/mock-idr.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ CREATE TABLE cms_vdm_view_mdcr_prd.v2_mdcr_bene_elctn_prd_usg (
116116
idr_updt_ts TIMESTAMPTZ
117117
);
118118

119+
CREATE TABLE cms_vdm_view_mdcr_prd.v2_mdcr_bene_xref (
120+
bene_hicn_num VARCHAR(11),
121+
bene_sk BIGINT NOT NULL,
122+
bene_xref_sk BIGINT NOT NULL,
123+
bene_kill_cred_cd VARCHAR(1) NOT NULL,
124+
idr_insrt_ts TIMESTAMPTZ NOT NULL,
125+
idr_updt_ts TIMESTAMPTZ,
126+
src_rec_ctre_ts TIMESTAMPTZ NOT NULL,
127+
PRIMARY KEY(bene_sk, bene_hicn_num, src_rec_ctre_ts)
128+
);
129+
119130
CREATE TABLE cms_vdm_view_mdcr_prd.v2_mdcr_cntrct_pbp_num (
120131
cntrct_pbp_sk BIGINT NOT NULL,
121132
cntrct_drug_plan_ind_cd VARCHAR(1),

apps/bfd-pipeline/bfd-pipeline-idr/model.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,28 @@ def fetch_query() -> str:
345345
{ORDER_BY}
346346
"""
347347

348+
class IdrBeneficiaryXref(IdrBaseModel):
349+
bene_hicn_num: Annotated[str, {PRIMARY_KEY: True}]
350+
bene_sk: Annotated[int, {PRIMARY_KEY: True}]
351+
bene_xref_sk: int
352+
bene_kill_cred_cd: Annotated[str, BeforeValidator(transform_default_string)]
353+
idr_insrt_ts: datetime
354+
idr_updt_ts: Annotated[datetime, {UPDATE_TIMESTAMP: True}, BeforeValidator(transform_null_date)]
355+
src_rec_ctre_ts: Annotated[datetime, {PRIMARY_KEY: True, BATCH_TIMESTAMP: True}]
356+
357+
@staticmethod
358+
def table():
359+
return "idr.beneficiary_xref"
360+
361+
@staticmethod
362+
def fetch_query() -> str:
363+
return """
364+
SELECT {COLUMNS}
365+
FROM cms_vdm_view_mdcr_prd.v2_mdcr_bene_xref
366+
{WHERE_CLAUSE}
367+
{ORDER_BY}
368+
"""
369+
348370

349371
class IdrElectionPeriodUsage(IdrBaseModel):
350372
bene_sk: Annotated[int, {PRIMARY_KEY: True}]

apps/bfd-pipeline/bfd-pipeline-idr/pipeline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
IdrBeneficiaryMbiId,
1212
IdrBeneficiaryStatus,
1313
IdrBeneficiaryThirdParty,
14+
IdrBeneficiaryXref,
1415
IdrClaim,
1516
IdrClaimAnsiSignature,
1617
IdrClaimDateSignature,
@@ -110,6 +111,7 @@ def run_pipeline(data_extractor: Extractor, connection_string: str):
110111
IdrBeneficiaryThirdParty,
111112
IdrBeneficiaryEntitlement,
112113
IdrBeneficiaryEntitlementReason,
114+
IdrBeneficiaryXref,
113115
IdrContractPbpNumber,
114116
IdrElectionPeriodUsage,
115117
IdrClaim,

apps/bfd-pipeline/bfd-pipeline-idr/test_pipeline.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ def test_pipeline(self, psql_url: str):
7171
assert rows[0]["bene_mbi_id"] == "1S000000000"
7272
assert rows[1]["bene_mbi_id"] == "8Z73WV0QC20"
7373

74+
cur = conn.execute("select * from idr.beneficiary_xref")
75+
assert cur.rowcount == 1
76+
rows = cur.fetchmany(1)
77+
assert rows[0]["bene_sk"] == 405764107
78+
7479
cur = conn.execute("select * from idr.claim")
7580
assert cur.rowcount == 1
7681
rows = cur.fetchmany(1)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bene_sk,bene_xref_sk,bene_hicn_num,src_rec_ctre_ts,idr_insrt_ts,idr_updt_ts,bene_kill_cred_cd
2+
405764107,405764107,123456789ZZ,2024-05-28 20:14:30.616999,2024-05-28 20:14:30.616999,2024-05-28 20:14:30.616999,0

apps/bfd-pipeline/bfd-pipeline-idr/test_samples2/SYNTHETIC_BENE.csv

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ BENE_SK,BENE_XREF_EFCTV_SK,BENE_MBI_ID,BENE_LAST_NAME,BENE_1ST_NAME,BENE_MIDL_NA
22
181968400,405764107,8Z73WV0QC20,Erdapfel,Bluey,Caroline,1967-03-19,,~,2,2,1970 S University Blvd,,,,,,Mobile,36609,AL,SPA,2023-08-15 15:47:22.203391,2024-01-12 02:41:38.810329,9999-12-31 00:00:00.000000
33
405764107,405764107,8Z73WV0QC20,Erdapfel,Bluey,Caroline,1967-03-19,,~,2,2,1970 S University Blvd,,,,,,Mobile,36609,AL,SPA,2024-03-30 06:52:39.094875,2024-05-28 20:14:30.616999,9999-12-31 00:00:00.000000
44
517782585,517782585,2B19C89AA35,Heeler,Alex,Joey,1959-03-14,,~,1,4,14152 ND-27,,,,,,Lisbon,58054,ND,~,2024-06-12 01:06:49.588129,2024-11-05 18:51:29.174693,9999-12-31 00:00:00.000000
5+
848484848,517782585,2B19C89AA35,Heeler,Alex,Joey,1959-03-14,,~,1,4,14152 ND-27,,,,,,Lisbon,58054,ND,~,2024-06-12 01:06:49.588129,2024-11-05 18:51:29.174693,9999-12-31 00:00:00.000000
6+
121212121,517782585,2B19C89AA35,Heeler,Alex,Joey,1959-03-14,,~,1,4,14152 ND-27,,,,,,Lisbon,58054,ND,~,2024-06-12 01:06:49.588129,2024-11-05 18:51:29.174693,9999-12-31 00:00:00.000000
7+
878934873,178083966,9OJ2CG2UC85,Jones,Abby,,1922-04-01,,~,1,6,2904 61st,,,,,,Galveston,77551,TX,~,2024-06-04 18:32:49.034231,2024-12-10 12:27:50.972975,9999-12-31T00:00:00.000000+0000
8+
792872340,178083966,9OJ2CG2UC85,Jones,Abby,,1922-04-01,,~,1,6,2904 61st,,,,,,Galveston,77551,TX,~,2024-06-04 18:32:49.034231,2024-12-10 12:27:50.972975,9999-12-31T00:00:00.000000+0000
9+
178083966,178083966,9OJ2CG2UC85,Jones,Abby,,1922-04-01,,~,1,6,2904 61st,,,,,,Galveston,77551,TX,~,2023-09-12 01:53:35.572554,2025-04-26 14:53:59.117910,9999-12-31T00:00:00.000000+0000
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bene_sk,bene_xref_sk,bene_hicn_num,src_rec_ctre_ts,idr_insrt_ts,idr_updt_ts,bene_kill_cred_cd
2+
878934873,178083966,123456789ZZ,2024-05-28 20:14:30.616999,2024-05-28 20:14:30.616999,2024-05-28 20:14:30.616999,1

apps/bfd-server-ng/src/main/java/gov/cms/bfd/server/ng/beneficiary/BeneficiaryRepository.java

Lines changed: 38 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,67 +20,43 @@
2020
public class BeneficiaryRepository {
2121
private EntityManager entityManager;
2222

23-
// TODO: this has not yet been thoroughly tested with edge cases.
24-
// It will likely need some adjustments.
25-
2623
/**
2724
* Queries for current and historical MBIs and BENE_SKs, along with their start/end dates.
25+
* Beneficiary records with kill credit switch set to "1" or overshare mbi are filtered out
2826
*
29-
* @param beneSk bene surrogate key
30-
* @return list of identities
27+
* @param beneXrefSk computed bene surrogate key
28+
* @return list of patient identities representing all active identities connected to the bene
29+
* record
3130
*/
32-
// This query has a few phases:
33-
// 1. Pull MBI information for the current bene_sk/mbi pair
34-
//
35-
// 2. Pull MBI information for historical bene_sk/mbi pairs (these must be two distinct steps
36-
// because there may not be a history record for the current MBI)
37-
//
38-
// 3. Use GROUP BY to filter out duplicates. There's additional info in these tables besides just
39-
// historical identity information, so there could be any number of duplicates relative to the
40-
// small amount of information we're pulling.
41-
//
42-
// NOTE - it would be simpler to do the WHERE NOT EXISTS on OvershareMBI after the UNION, but
43-
// that doesn't appear to be supported by the JQL parser.
44-
public List<PatientIdentity> getPatientIdentities(long beneSk) {
31+
public List<PatientIdentity> getValidBeneficiaryIdentities(long beneXrefSk) {
4532
return entityManager
4633
.createQuery(
4734
"""
48-
WITH allBeneInfo AS (
49-
SELECT
50-
bene.beneSk beneSk,
51-
bene.xrefSk xrefSk,
52-
bene.identity.mbi mbi,
53-
mbiId.effectiveDate effectiveDate,
54-
mbiId.obsoleteDate obsoleteDate
55-
FROM
56-
Beneficiary bene
57-
LEFT JOIN BeneficiaryMbiId mbiId
58-
ON bene.identity.mbi = mbiId.mbi
59-
AND mbiId.obsoleteDate < gov.cms.bfd.server.ng.IdrConstants.DEFAULT_DATE
60-
WHERE bene.beneSk = :beneSk
61-
AND NOT EXISTS(SELECT 1 FROM OvershareMbi om WHERE om.mbi = bene.identity.mbi)
62-
UNION
63-
SELECT
64-
beneHistory.beneSk beneSk,
65-
beneHistory.xrefSk xrefSk,
66-
beneHistory.mbi mbi,
67-
mbiId.effectiveDate effectiveDate,
68-
mbiId.obsoleteDate obsoleteDate
69-
FROM Beneficiary bene
70-
JOIN BeneficiaryHistory beneHistory
71-
ON beneHistory.xrefSk = bene.xrefSk
72-
LEFT JOIN BeneficiaryMbiId mbiId
73-
ON mbiId.mbi = beneHistory.mbi
74-
AND mbiId.obsoleteDate < gov.cms.bfd.server.ng.IdrConstants.DEFAULT_DATE
75-
WHERE bene.beneSk = :beneSk
76-
AND NOT EXISTS(SELECT 1 FROM OvershareMbi om WHERE om.mbi = bene.identity.mbi)
35+
SELECT new PatientIdentity(
36+
ROW_NUMBER() OVER (ORDER BY bene.beneSk) rowId,
37+
bene.beneSk,
38+
bene.xrefSk,
39+
bene.identity.mbi,
40+
mbiId.effectiveDate,
41+
mbiId.obsoleteDate
7742
)
78-
SELECT new PatientIdentity(ROW_NUMBER() OVER (ORDER BY abi.beneSk) rowId, abi.beneSk, abi.xrefSk, abi.mbi, abi.effectiveDate, abi.obsoleteDate)
79-
FROM allBeneInfo abi
80-
GROUP BY abi.beneSk, abi.mbi, abi.xrefSk, abi.effectiveDate, abi.obsoleteDate
43+
FROM Beneficiary bene
44+
LEFT JOIN BeneficiaryMbiId mbiId
45+
ON bene.identity.mbi = mbiId.mbi
46+
AND mbiId.obsoleteDate < gov.cms.bfd.server.ng.IdrConstants.DEFAULT_DATE
47+
WHERE bene.xrefSk = :beneXrefSk
48+
AND NOT EXISTS(SELECT 1 FROM OvershareMbi om WHERE om.mbi = bene.identity.mbi)
49+
AND NOT EXISTS (
50+
SELECT 1
51+
FROM BeneficiaryXref bx
52+
WHERE bx.beneSk = bene.beneSk
53+
AND bx.beneXrefSk = bene.xrefSk
54+
AND bx.beneKillCred = '1'
55+
)
56+
GROUP BY bene.beneSk, bene.xrefSk, bene.identity.mbi, mbiId.effectiveDate, mbiId.obsoleteDate
8157
""",
8258
PatientIdentity.class)
83-
.setParameter("beneSk", beneSk)
59+
.setParameter("beneXrefSk", beneXrefSk)
8460
.getResultList();
8561
}
8662

@@ -130,10 +106,10 @@ public Optional<Long> getXrefBeneSk(long beneSk) {
130106
return entityManager
131107
.createQuery(
132108
"""
133-
SELECT b.xrefSk
134-
FROM Beneficiary b
135-
WHERE b.beneSk = :beneSk
136-
AND NOT EXISTS(SELECT 1 FROM OvershareMbi om WHERE om.mbi = b.identity.mbi)
109+
SELECT bene.xrefSk
110+
FROM Beneficiary bene
111+
WHERE bene.beneSk = :beneSk
112+
AND NOT EXISTS(SELECT 1 FROM OvershareMbi om WHERE om.mbi = bene.identity.mbi)
137113
""",
138114
Long.class)
139115
.setParameter("beneSk", beneSk)
@@ -172,13 +148,13 @@ private Optional<Beneficiary> searchBeneficiary(
172148
.createQuery(
173149
String.format(
174150
"""
175-
SELECT b
176-
FROM Beneficiary b
177-
WHERE b.%s = :id
178-
AND ((cast(:lowerBound AS ZonedDateTime)) IS NULL OR b.meta.updatedTimestamp %s :lowerBound)
179-
AND ((cast(:upperBound AS ZonedDateTime)) IS NULL OR b.meta.updatedTimestamp %s :upperBound)
180-
AND NOT EXISTS(SELECT 1 FROM OvershareMbi om WHERE om.mbi = b.identity.mbi)
181-
ORDER BY b.obsoleteTimestamp DESC
151+
SELECT bene
152+
FROM Beneficiary bene
153+
WHERE bene.%s = :id
154+
AND ((cast(:lowerBound AS ZonedDateTime)) IS NULL OR bene.meta.updatedTimestamp %s :lowerBound)
155+
AND ((cast(:upperBound AS ZonedDateTime)) IS NULL OR bene.meta.updatedTimestamp %s :upperBound)
156+
AND NOT EXISTS(SELECT 1 FROM OvershareMbi om WHERE om.mbi = bene.identity.mbi)
157+
ORDER BY bene.obsoleteTimestamp DESC
182158
""",
183159
idColumnName,
184160
lastUpdatedRange.getLowerBoundSqlOperator(),

0 commit comments

Comments
 (0)