Skip to content

Commit ba442f2

Browse files
authored
Merge pull request #1145 from CMSgov/feature/QPPSF-8394_CT-Error-message-updates
QPPSF-8394: Fix for performance period error messaging
2 parents 97f6076 + 7f79412 commit ba442f2

File tree

9 files changed

+135
-15
lines changed

9 files changed

+135
-15
lines changed

ERROR_MESSAGES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ Any text in the following format `(Example)` are considered variables to be fill
4848
* 51 : CT - A single measure performed value is required and must be either a Y or an N.
4949
* 52 : CT - The measure data with population id '`(population id)`' must have exactly one Aggregate Count.
5050
* 53 : CT - Measure data with population id '`(population id)`' must be a whole number greater than or equal to 0
51-
* 55 : CT - A CPC Plus or PCF Performance period start must be 01/01/2021. Please refer to the IG for more information here: https://ecqi.healthit.gov/sites/default/files/2021-CMS-QRDA-III-Eligible-Clinicians-and-EP-IG-v1.3.pdf#page=14
52-
* 56 : CT - A CPC Plus or PCF Performance period end must be 12/31/2021. Please refer to the IG for more information here: https://ecqi.healthit.gov/sites/default/files/2021-CMS-QRDA-III-Eligible-Clinicians-and-EP-IG-v1.3.pdf#page=14
51+
* 55 : CT - A `(Program name)` Performance period start must be 01/01/2021. Please refer to the IG for more information here: https://ecqi.healthit.gov/sites/default/files/2021-CMS-QRDA-III-Eligible-Clinicians-and-EP-IG-v1.3.pdf#page=14
52+
* 56 : CT - A `(Program name)` Performance period end must be 12/31/2021. Please refer to the IG for more information here: https://ecqi.healthit.gov/sites/default/files/2021-CMS-QRDA-III-Eligible-Clinicians-and-EP-IG-v1.3.pdf#page=14
5353
* 57 : CT - The measure reference results must have a single measure population
5454
* 58 : CT - The measure reference results must have a single measure type
5555
* 59 : CT - The electronic measure id: `(Current eMeasure ID)` requires a `(Subpopulation type)` with the correct UUID of `(Correct uuid required)`. Here is a link to the IG containing all the valid measure ids: https://ecqi.healthit.gov/sites/default/files/2021-CMS-QRDA-III-Eligible-Clinicians-and-EP-IG-v1.3.pdf#page=40

commons/src/main/java/gov/cms/qpp/conversion/model/error/ProblemCode.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ public enum ProblemCode implements LocalizedProblem {
116116
+ "Aggregate Count.", true),
117117
MEASURE_DATA_VALUE_NOT_INTEGER(53, "Measure data with population id '`(population id)`' "
118118
+ "must be a whole number greater than or equal to 0", true),
119-
CPC_PCF_PERFORMANCE_PERIOD_START(55, "A CPC Plus or PCF Performance period start must be 01/01/2021. "
120-
+ "Please refer to the IG for more information here: " + DocumentationReference.CPC_PLUS_SUBMISSIONS),
121-
CPC_PCF_PERFORMANCE_PERIOD_END(56, "A CPC Plus or PCF Performance period end must be 12/31/2021. "
122-
+ "Please refer to the IG for more information here: " + DocumentationReference.CPC_PLUS_SUBMISSIONS),
119+
CPC_PCF_PERFORMANCE_PERIOD_START(55, "A `(Program name)` Performance period start must be 01/01/2021. "
120+
+ "Please refer to the IG for more information here: " + DocumentationReference.CPC_PLUS_SUBMISSIONS, true),
121+
CPC_PCF_PERFORMANCE_PERIOD_END(56, "A `(Program name)` Performance period end must be 12/31/2021. "
122+
+ "Please refer to the IG for more information here: " + DocumentationReference.CPC_PLUS_SUBMISSIONS, true),
123123
QUALITY_MEASURE_ID_MISSING_SINGLE_MEASURE_POPULATION(57, "The measure reference results must have a single "
124124
+ "measure population"),
125125
QUALITY_MEASURE_ID_MISSING_SINGLE_MEASURE_TYPE(58, "The measure reference results must have a single "
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package gov.cms.qpp.conversion.util;
2+
3+
import gov.cms.qpp.conversion.model.Node;
4+
import gov.cms.qpp.conversion.model.TemplateId;
5+
6+
public class NodeHelper {
7+
private NodeHelper() {
8+
//private and empty because this is a utility class
9+
}
10+
11+
/**
12+
* Find a specific parent node of current node given
13+
*
14+
* @param node
15+
* @param templateId
16+
* @return
17+
*/
18+
public static Node findParent(Node node, TemplateId templateId) {
19+
Node currentParent = node.getParent();
20+
Node currentNode = new Node(templateId);
21+
if (currentParent != null && templateId.getRoot().equalsIgnoreCase(currentParent.getType().getRoot())) {
22+
currentNode = currentParent;
23+
} else if ( currentParent != null) {
24+
currentNode = findParent(currentParent, templateId);
25+
}
26+
return currentNode;
27+
}
28+
}

converter/src/main/java/gov/cms/qpp/conversion/validate/CpcPerformancePeriodValidation.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package gov.cms.qpp.conversion.validate;
22

33
import gov.cms.qpp.conversion.Context;
4+
import gov.cms.qpp.conversion.decode.ClinicalDocumentDecoder;
45
import gov.cms.qpp.conversion.decode.ReportingParametersActDecoder;
56
import gov.cms.qpp.conversion.model.Node;
67
import gov.cms.qpp.conversion.model.Program;
78
import gov.cms.qpp.conversion.model.TemplateId;
89
import gov.cms.qpp.conversion.model.Validator;
910
import gov.cms.qpp.conversion.model.error.ProblemCode;
11+
import gov.cms.qpp.conversion.util.NodeHelper;
12+
13+
import java.util.Locale;
1014

1115
/**
1216
* Validates the QRDA Category III Report Node's national provider identifier/taxpayer identification number combinations
@@ -24,10 +28,13 @@ public class CpcPerformancePeriodValidation extends NodeValidator {
2428
*/
2529
@Override
2630
protected void performValidation(Node node) {
31+
Node clinicalDocument = NodeHelper.findParent(node, TemplateId.CLINICAL_DOCUMENT);
32+
String programName = clinicalDocument.getValue(ClinicalDocumentDecoder.PROGRAM_NAME).toUpperCase(Locale.ROOT);
33+
2734
checkErrors(node)
28-
.valueIs(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START,
35+
.valueIs(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START.format(programName),
2936
ReportingParametersActDecoder.PERFORMANCE_START, REPORTING_PERIOD_START)
30-
.valueIs(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END,
37+
.valueIs(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END.format(programName),
3138
ReportingParametersActDecoder.PERFORMANCE_END, REPORTING_PERIOD_END);
3239
}
3340
}

converter/src/test/java/gov/cms/qpp/acceptance/NegativePcfRoundTripTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.nio.file.Paths;
2222
import java.util.ArrayList;
2323
import java.util.List;
24+
import java.util.Locale;
2425

2526
import static com.google.common.truth.Truth.assertThat;
2627

@@ -82,10 +83,10 @@ void testPcfInvalidPerformancePeriod() {
8283
List<Detail> details = conversionError(Y5_NEGATIVE_PCF);
8384

8485
assertThat(details).comparingElementsUsing(DetailsErrorEquals.INSTANCE)
85-
.contains(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START.getProblemCode());
86+
.contains(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START.format(ClinicalDocumentDecoder.PCF_PROGRAM_NAME.toUpperCase(Locale.ROOT)));
8687

8788
assertThat(details).comparingElementsUsing(DetailsErrorEquals.INSTANCE)
88-
.contains(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END.getProblemCode());
89+
.contains(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END.format(ClinicalDocumentDecoder.PCF_PROGRAM_NAME.toUpperCase(Locale.ROOT)));
8990
}
9091

9192
List<Detail> conversionError(Path path) {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package gov.cms.qpp.conversion.util;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import gov.cms.qpp.conversion.model.Node;
6+
import gov.cms.qpp.conversion.model.TemplateId;
7+
8+
import java.lang.reflect.Constructor;
9+
10+
import static com.google.common.truth.Truth.assertThat;
11+
import static com.google.common.truth.Truth.assertWithMessage;
12+
13+
public class NodeHelperTest {
14+
15+
@Test
16+
void privateConstructorTest() throws Exception {
17+
// reflection concept to get constructor of a Singleton class.
18+
Constructor<NodeHelper> constructor = NodeHelper.class.getDeclaredConstructor();
19+
// change the accessibility of constructor for outside a class object creation.
20+
constructor.setAccessible(true);
21+
// creates object of a class as constructor is accessible now.
22+
NodeHelper nodeHelper = constructor.newInstance();
23+
// close the accessibility of a constructor.
24+
constructor.setAccessible(false);
25+
26+
assertWithMessage("Expect to have an instance here ")
27+
.that(nodeHelper).isInstanceOf(NodeHelper.class);
28+
}
29+
30+
@Test
31+
void findParentTest() {
32+
Node grandParentNode = new Node(TemplateId.CLINICAL_DOCUMENT);
33+
Node parentNode = new Node(TemplateId.MEASURE_SECTION_V4);
34+
parentNode.setParent(grandParentNode);
35+
36+
Node testNode = new Node(TemplateId.REPORTING_PARAMETERS_ACT);
37+
testNode.setParent(parentNode);
38+
39+
Node expectedGrandparent = NodeHelper.findParent(testNode, grandParentNode.getType());
40+
Node expectedParent = NodeHelper.findParent(testNode, parentNode.getType());
41+
42+
assertThat(expectedGrandparent.getType()).isSameInstanceAs(TemplateId.CLINICAL_DOCUMENT);
43+
assertThat(expectedParent.getType()).isSameInstanceAs(TemplateId.MEASURE_SECTION_V4);
44+
}
45+
}

converter/src/test/java/gov/cms/qpp/conversion/validate/CpcPerformancePeriodValidationTest.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import static com.google.common.truth.Truth.assertWithMessage;
44

55
import java.util.List;
6+
import java.util.Locale;
67

78
import org.junit.jupiter.api.BeforeEach;
89
import org.junit.jupiter.api.Test;
910

11+
import gov.cms.qpp.conversion.decode.ClinicalDocumentDecoder;
1012
import gov.cms.qpp.conversion.decode.ReportingParametersActDecoder;
1113
import gov.cms.qpp.conversion.model.Node;
1214
import gov.cms.qpp.conversion.model.TemplateId;
@@ -18,14 +20,23 @@ class CpcPerformancePeriodValidationTest {
1820

1921
private CpcPerformancePeriodValidation cpcValidator;
2022
private Node node;
23+
final private String programName = ClinicalDocumentDecoder.CPCPLUS_PROGRAM_NAME.toUpperCase(Locale.ROOT);
2124

2225
@BeforeEach
2326
void setup() {
2427
cpcValidator = new CpcPerformancePeriodValidation();
28+
29+
Node clinicalDocument = new Node(TemplateId.CLINICAL_DOCUMENT);
30+
clinicalDocument.putValue(ClinicalDocumentDecoder.PROGRAM_NAME, programName);
31+
32+
Node measureSection = new Node(TemplateId.MEASURE_SECTION_V4);
33+
measureSection.setParent(clinicalDocument);
34+
2535
node = new Node(TemplateId.REPORTING_PARAMETERS_ACT);
2636
node.putValue(ReportingParametersActDecoder.PERFORMANCE_YEAR, "2021");
2737
node.putValue(ReportingParametersActDecoder.PERFORMANCE_START, "20210101");
2838
node.putValue(ReportingParametersActDecoder.PERFORMANCE_END, "20211231");
39+
node.setParent(measureSection);
2940
}
3041

3142
@Test
@@ -42,7 +53,7 @@ void testPerformancePeriodStartIsInvalid() {
4253

4354
assertWithMessage("Should result in a performance start error")
4455
.that(details).comparingElementsUsing(DetailsErrorEquals.INSTANCE)
45-
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START);
56+
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START.format(programName));
4657
}
4758

4859
@Test
@@ -51,6 +62,6 @@ void testPerformancePeriodEndIsInvalid() {
5162
List<Detail> details = cpcValidator.validateSingleNode(node).getErrors();
5263
assertWithMessage("Should result in a performance end error")
5364
.that(details).comparingElementsUsing(DetailsErrorEquals.INSTANCE)
54-
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END);
65+
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END.format(programName));
5566
}
5667
}

converter/src/test/java/gov/cms/qpp/conversion/validate/PcfPerformancePeriodValidationTest.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.junit.jupiter.api.BeforeEach;
44
import org.junit.jupiter.api.Test;
55

6+
import gov.cms.qpp.conversion.decode.ClinicalDocumentDecoder;
67
import gov.cms.qpp.conversion.decode.ReportingParametersActDecoder;
78
import gov.cms.qpp.conversion.model.Node;
89
import gov.cms.qpp.conversion.model.TemplateId;
@@ -11,6 +12,7 @@
1112
import gov.cms.qpp.conversion.model.error.correspondence.DetailsErrorEquals;
1213

1314
import java.util.List;
15+
import java.util.Locale;
1416

1517
import static com.google.common.truth.Truth.assertThat;
1618
import static com.google.common.truth.Truth.assertWithMessage;
@@ -19,14 +21,22 @@ public class PcfPerformancePeriodValidationTest {
1921

2022
private PcfPerformancePeriodValidation validator;
2123
private Node node;
24+
private String programName = ClinicalDocumentDecoder.PCF_PROGRAM_NAME.toUpperCase(Locale.ROOT);
2225

2326
@BeforeEach
2427
void setup() {
2528
validator = new PcfPerformancePeriodValidation();
29+
Node clinicalDocument = new Node(TemplateId.CLINICAL_DOCUMENT);
30+
clinicalDocument.putValue(ClinicalDocumentDecoder.PROGRAM_NAME, programName);
31+
32+
Node measureSection = new Node(TemplateId.MEASURE_SECTION_V4);
33+
measureSection.setParent(clinicalDocument);
34+
2635
node = new Node(TemplateId.REPORTING_PARAMETERS_ACT);
2736
node.putValue(ReportingParametersActDecoder.PERFORMANCE_YEAR, "2021");
2837
node.putValue(ReportingParametersActDecoder.PERFORMANCE_START, "20210101");
2938
node.putValue(ReportingParametersActDecoder.PERFORMANCE_END, "20211231");
39+
node.setParent(measureSection);
3040
}
3141

3242
@Test
@@ -42,14 +52,14 @@ void testPerformancePeriodStartIsInvalid() {
4252
List<Detail> details = validator.validateSingleNode(node).getErrors();
4353

4454
assertThat(details).comparingElementsUsing(DetailsErrorEquals.INSTANCE)
45-
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START);
55+
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_START.format(programName));
4656
}
4757

4858
@Test
4959
void testPerformancePeriodEndIsInvalid() {
5060
node.putValue(ReportingParametersActDecoder.PERFORMANCE_END, "not what we want");
5161
List<Detail> details = validator.validateSingleNode(node).getErrors();
5262
assertThat(details).comparingElementsUsing(DetailsErrorEquals.INSTANCE)
53-
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END);
63+
.containsExactly(ProblemCode.CPC_PCF_PERFORMANCE_PERIOD_END.format(programName));
5464
}
5565
}

converter/src/test/resources/cpc_plus/failure/fixture.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,20 @@
183183
]
184184
},
185185
"CPCPlus_InvalidPerformancePeriod_SampleQRDA-III.xml": {
186-
"strict": true,
186+
"strict": false,
187187
"errorData": [
188188
{
189189
"errorCode": 55,
190+
"subs": [
191+
".*"
192+
],
190193
"occurrences": 1
191194
},
192195
{
193196
"errorCode": 56,
197+
"subs": [
198+
".*"
199+
],
194200
"occurrences": 1
195201
}
196202
]
@@ -338,6 +344,9 @@
338344
"errorData": [
339345
{
340346
"errorCode": 56,
347+
"subs": [
348+
".*"
349+
],
341350
"occurrences": 1
342351
}
343352
]
@@ -347,6 +356,9 @@
347356
"errorData": [
348357
{
349358
"errorCode": 55,
359+
"subs": [
360+
".*"
361+
],
350362
"occurrences": 1
351363
}
352364
]
@@ -863,10 +875,16 @@
863875
"errorData" : [
864876
{
865877
"errorCode" : 56,
878+
"subs": [
879+
".*"
880+
],
866881
"occurrences": 1
867882
},
868883
{
869884
"errorCode" : 55,
885+
"subs": [
886+
".*"
887+
],
870888
"occurrences": 1
871889
}
872890
]

0 commit comments

Comments
 (0)