diff --git a/Dockerfile b/Dockerfile index 1345d2ac9..eb678d5eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:17 +FROM eclipse-temurin:17 AS builder ARG MAVEN_VERSION=3.9.6 ARG USER_HOME_DIR="/root" @@ -18,19 +18,26 @@ ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2" COPY mvn-entrypoint.sh /usr/local/bin/mvn-entrypoint.sh COPY settings-docker.xml /usr/share/maven/ref/ -ENTRYPOINT ["/usr/local/bin/mvn-entrypoint.sh"] -CMD ["mvn"] +# Copy src files needed for build +COPY pom.xml /usr/src/app/ +COPY commons/ /usr/src/app/commons/ +COPY test-commons/ /usr/src/app/test-commons/ +COPY converter/ /usr/src/app/converter/ +COPY commandline/ /usr/src/app/commandline/ +COPY rest-api/ /usr/src/app/rest-api/ +COPY generate/ /usr/src/app/generate/ +COPY test-coverage/ /usr/src/app/test-coverage/ +COPY tools/docker/docker-artifacts/ /usr/src/app/tools/docker/docker-artifacts/ +WORKDIR /usr/src/app/ -RUN mkdir -p /usr/src/app/ -RUN mkdir -p /usr/src/run/ +RUN /usr/local/bin/mvn-entrypoint.sh mvn install -Dmaven.test.skip -Djacoco.skip=true > /dev/null -COPY ./ /usr/src/app/ +# Final stage +FROM eclipse-temurin:17-jre -WORKDIR /usr/src/app/ - -RUN cp -r ./tools/docker/docker-artifacts/* /usr/src/run/ -RUN mvn install -Dmaven.test.skip -Djacoco.skip=true > /dev/null -RUN cp ./rest-api/target/rest-api.jar /usr/src/run/ +RUN mkdir -p /usr/src/run/ +COPY --from=builder /usr/src/app/tools/docker/docker-artifacts /usr/src/run/ +COPY --from=builder /usr/src/app/rest-api/target/rest-api.jar /usr/src/run/ WORKDIR /usr/src/run/ diff --git a/DockerfileTest b/DockerfileTest index 8f698d5b2..fb111ff24 100644 --- a/DockerfileTest +++ b/DockerfileTest @@ -1,16 +1,18 @@ -FROM maven:3.9.6-eclipse-temurin-17 - -RUN mkdir -p /usr/src/app/ -RUN mkdir -p /usr/src/run/ -RUN apt-get update && apt-get install dos2unix +FROM maven:3.9.6-eclipse-temurin-17 AS builder COPY ./ /usr/src/app/ - WORKDIR /usr/src/app/ RUN mvn install -Dmaven.test.skip -Djacoco.skip=true > /dev/null -RUN cp ./rest-api/target/rest-api.jar /usr/src/run/ -RUN cp -r ./tools/docker/docker-test-artifacts/* /usr/src/run/ + +# Final stage +FROM eclipse-temurin:17-jre + +RUN apt-get update && apt-get install -y dos2unix && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /usr/src/run/ +COPY --from=builder /usr/src/app/rest-api/target/rest-api.jar /usr/src/run/ +COPY --from=builder /usr/src/app/tools/docker/docker-test-artifacts/* /usr/src/run/ RUN dos2unix /usr/src/run/qppConverterTest.sh WORKDIR /usr/src/run/ diff --git a/ERROR_MESSAGES.md b/ERROR_MESSAGES.md index 0deeb3b8d..c5e755d0f 100644 --- a/ERROR_MESSAGES.md +++ b/ERROR_MESSAGES.md @@ -91,3 +91,4 @@ Any text in the following format `(Example)` are considered variables to be fill * 106 : CT - Promoting Interoperability data should not be reported in a PCF QRDA III file. * 107 : CT - There's missing NPI/TIN combination. The NPI/TIN `(npi)`-`(tin)` was active on the PCF practitioner roster for `(apm)` during the performance year but was not found in the file. Ensure your submission contains all NPI/TIN combinations that were active on your roster at any point during the performance year. Your QRDA III file and/or roster may require updates. The QPP website doesn't have access to roster updates made after December 13, 2025. It's critical to ensure your roster is up to date and your QRDA III file contains all NPI/TIN values that were active on your roster during the performance year. Contact your health IT vendor if your QRDA III file requires updates. You can find instructions on updating rosters in the PCF Practice Management Guide: (https://cmmi.my.salesforce.com/sfc/p/#i0000000iryR/a/t00000028RsP/dMF_romOmf5VLe7p5lUj8vch11mPmELP6ZuyI16vS.Y). * 108 : CT - Found an unexpected NPI/TIN combination. The NPI/TIN `(npi)`-`(tin)` was reported in the file but does not exist at the practice or was not active on the PCF practitioner roster for `(apm)` during the performance year. Ensure your submission only contains NPI/TIN combinations that were active on your roster at any point during the performance year. Your QRDA III file and/or roster may require updates. Note: The QPP website does not have access to roster updates made after December 13, 2025. It's critical that you ensure your roster is up to date and your QRDA III file contains all NPI/TIN values that were active on your roster during the performance year. Please contact your health IT vendor if your QRDA III file requires updates. You can find instructions on how updating rosters in the PCF Practice Management Guide (https://cmmi.my.salesforce.com/sfc/p/#i0000000iryR/a/t00000028RsP/dMF_romOmf5VLe7p5lUj8vch11mPmELP6ZuyI16vS.Y). +* 109 : CT - Review the Clinical Document for program `(program name)`. Only the Promoting Interoperability (PI) measure category is permitted; found `(provided category)`. diff --git a/acceptance-tests/pom.xml b/acceptance-tests/pom.xml index 16f18f5e5..040b732e7 100644 --- a/acceptance-tests/pom.xml +++ b/acceptance-tests/pom.xml @@ -3,7 +3,7 @@ 4.0.0 acceptance-tests gov.cms.qpp.conversion - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE conversion-tests jar diff --git a/commandline/pom.xml b/commandline/pom.xml index 7cc9116f7..15223bd1e 100644 --- a/commandline/pom.xml +++ b/commandline/pom.xml @@ -6,7 +6,7 @@ gov.cms.qpp.conversion qpp-conversion-tool-parent - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../pom.xml diff --git a/commons/pom.xml b/commons/pom.xml index ef8478f31..fd511e2bc 100644 --- a/commons/pom.xml +++ b/commons/pom.xml @@ -6,7 +6,7 @@ gov.cms.qpp.conversion qpp-conversion-tool-parent - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../pom.xml diff --git a/commons/src/main/java/gov/cms/qpp/conversion/model/error/ProblemCode.java b/commons/src/main/java/gov/cms/qpp/conversion/model/error/ProblemCode.java index f395454fb..8988a8d78 100644 --- a/commons/src/main/java/gov/cms/qpp/conversion/model/error/ProblemCode.java +++ b/commons/src/main/java/gov/cms/qpp/conversion/model/error/ProblemCode.java @@ -203,7 +203,10 @@ public enum ProblemCode implements LocalizedProblem { + "all NPI/TIN values that were active on your roster during the performance year. " + "Please contact your health IT vendor if your QRDA III file requires updates. " + "You can find instructions on how updating rosters in the PCF Practice Management Guide " - + "(https://cmmi.my.salesforce.com/sfc/p/#i0000000iryR/a/t00000028RsP/dMF_romOmf5VLe7p5lUj8vch11mPmELP6ZuyI16vS.Y).", true); + + "(https://cmmi.my.salesforce.com/sfc/p/#i0000000iryR/a/t00000028RsP/dMF_romOmf5VLe7p5lUj8vch11mPmELP6ZuyI16vS.Y).", true), + SSP_PI_ONLY_MEASURE_CATEGORY(109, "Review the Clinical Document for program `(program name)`. " + + "Only the Promoting Interoperability (PI) measure category is permitted; " + + "found `(provided category)`.", true); private static final Map CODE_TO_VALUE = Arrays.stream(values()) .collect(Collectors.toMap(ProblemCode::getCode, Function.identity())); diff --git a/commons/src/main/resources/measures-data.json b/commons/src/main/resources/measures-data.json index 523e52ff5..156bd7e37 100644 --- a/commons/src/main/resources/measures-data.json +++ b/commons/src/main/resources/measures-data.json @@ -24900,7 +24900,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-chron-copd.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-chron-copd.pdf" }, "allowedPrograms": [ "mips", @@ -24927,7 +24927,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-aki-new-hd.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-aki-new-hd.pdf" }, "allowedPrograms": [ "mips", @@ -24953,7 +24953,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-rleccli.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-rleccli.pdf" }, "allowedPrograms": [ "mips" @@ -24978,7 +24978,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-ckd.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-ckd.pdf" }, "allowedPrograms": [ "mips", @@ -25004,7 +25004,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-ip-copd.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-ip-copd.pdf" }, "allowedPrograms": [ "mips", @@ -25030,7 +25030,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-colrec-rsct.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-colrec-rsct.pdf" }, "allowedPrograms": [ "mips", @@ -25056,7 +25056,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-diabetes.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-diabetes.pdf" }, "allowedPrograms": [ "mips", @@ -25082,7 +25082,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-depression.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-depression.pdf" }, "allowedPrograms": [ "mips", @@ -25109,7 +25109,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-emergency.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-emergency.pdf" }, "allowedPrograms": [ "mips", @@ -25135,7 +25135,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-op-pci.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-op-pci.pdf" }, "allowedPrograms": [ "mips", @@ -25161,7 +25161,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-esrd.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-esrd.pdf" }, "allowedPrograms": [ "mips", @@ -25187,7 +25187,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-fihr.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-fihr.pdf" }, "allowedPrograms": [ "mips", @@ -25213,7 +25213,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-hd-access.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-hd-access.pdf" }, "allowedPrograms": [ "mips" @@ -25238,7 +25238,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-heart-fail.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-heart-fail.pdf" }, "allowedPrograms": [ "mips", @@ -25265,7 +25265,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-ich-cva.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-ich-cva.pdf" }, "allowedPrograms": [ "mips", @@ -25291,7 +25291,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-cataract1.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-cataract1.pdf" }, "allowedPrograms": [ "mips", @@ -25317,7 +25317,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-knee-arthro.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-knee-arthro.pdf" }, "allowedPrograms": [ "mips", @@ -25343,7 +25343,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-kidney-tplt.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-kidney-tplt.pdf" }, "allowedPrograms": [ "mips", @@ -25369,7 +25369,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-low-back.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-low-back.pdf" }, "allowedPrograms": [ "mips", @@ -25395,7 +25395,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-lgi-bleed.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-lgi-bleed.pdf" }, "allowedPrograms": [ "mips" @@ -25420,7 +25420,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-lump-mast.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-lump-mast.pdf" }, "allowedPrograms": [ "mips", @@ -25446,7 +25446,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-l-fusion.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-l-fusion.pdf" }, "allowedPrograms": [ "mips", @@ -25472,7 +25472,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-mel-rsct.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-mel-rsct.pdf" }, "allowedPrograms": [ "mips", @@ -25498,7 +25498,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-ne-cabg.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-ne-cabg.pdf" }, "allowedPrograms": [ "mips", @@ -25524,7 +25524,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-prost-ca.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-prost-ca.pdf" }, "allowedPrograms": [ "mips", @@ -25551,7 +25551,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-el-ha.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-el-ha.pdf" }, "allowedPrograms": [ "mips", @@ -25577,7 +25577,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-psych.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-psych.pdf" }, "allowedPrograms": [ "mips", @@ -25603,7 +25603,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-rheum-arth.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-rheum-arth.pdf" }, "allowedPrograms": [ "mips", @@ -25629,7 +25629,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-resp-infxn.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-resp-infxn.pdf" }, "allowedPrograms": [ "mips" @@ -25654,7 +25654,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-rn-stone.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-rn-stone.pdf" }, "allowedPrograms": [ "mips", @@ -25680,7 +25680,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-sepsis.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-sepsis.pdf" }, "allowedPrograms": [ "mips" @@ -25705,7 +25705,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-ss-clnscpy.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-ss-clnscpy.pdf" }, "allowedPrograms": [ "mips", @@ -25731,7 +25731,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-ebcm-ip-pci.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-ebcm-ip-pci.pdf" }, "allowedPrograms": [ "mips", @@ -25757,7 +25757,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-mspb-clinician.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-mspb-clinician.pdf" }, "allowedPrograms": [ "mips", @@ -25790,7 +25790,7 @@ "administrativeClaims" ], "measureSpecification": { - "default": "https://qpp.cms.gov/docs/cost_specifications/2024-12-py2025-mif-tpcc.pdf" + "default": "https://qpp.cms.gov/docs/cost_specifications/2025-06-py2025-mif-tpcc.pdf" }, "allowedPrograms": [ "mips", diff --git a/converter/pom.xml b/converter/pom.xml index dda9ab58e..f07e6d615 100644 --- a/converter/pom.xml +++ b/converter/pom.xml @@ -6,7 +6,7 @@ gov.cms.qpp.conversion qpp-conversion-tool-parent - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../pom.xml @@ -170,7 +170,7 @@ gov.cms.qpp.conversion commons - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE compile diff --git a/converter/src/main/java/gov/cms/qpp/conversion/decode/ClinicalDocumentDecoder.java b/converter/src/main/java/gov/cms/qpp/conversion/decode/ClinicalDocumentDecoder.java index e1e092bec..99a018397 100644 --- a/converter/src/main/java/gov/cms/qpp/conversion/decode/ClinicalDocumentDecoder.java +++ b/converter/src/main/java/gov/cms/qpp/conversion/decode/ClinicalDocumentDecoder.java @@ -258,15 +258,15 @@ private Pair getProgramNameEntityPair(String name) { break; case SSP_PI_INDIVIDUAL: - pair = new ImmutablePair<>(SSP_PI_INDIVIDUAL, ENTITY_INDIVIDUAL); + pair = new ImmutablePair<>(SSP_PROGRAM_NAME, ENTITY_INDIVIDUAL); break; case SSP_PI_GROUP: - pair = new ImmutablePair<>(SSP_PI_GROUP, ENTITY_GROUP); + pair = new ImmutablePair<>(SSP_PROGRAM_NAME, ENTITY_GROUP); break; case SSP_PI_APM: - pair = new ImmutablePair<>(SSP_PI_APM, ENTITY_APM); + pair = new ImmutablePair<>(SSP_PROGRAM_NAME, ENTITY_APM); break; default: diff --git a/converter/src/main/java/gov/cms/qpp/conversion/encode/ClinicalDocumentEncoder.java b/converter/src/main/java/gov/cms/qpp/conversion/encode/ClinicalDocumentEncoder.java index 53f202d3c..735392712 100755 --- a/converter/src/main/java/gov/cms/qpp/conversion/encode/ClinicalDocumentEncoder.java +++ b/converter/src/main/java/gov/cms/qpp/conversion/encode/ClinicalDocumentEncoder.java @@ -79,7 +79,7 @@ private void encodeToplevel(JsonWrapper wrapper, Node thisNode) { wrapper.put(ENTITY_ID, thisNode.getValue(ENTITY_ID)); } - if ((Program.isApp(thisNode) || Program.isMips(thisNode) || Program.isAppPlus(thisNode)) + if ((Program.isApp(thisNode) || Program.isMips(thisNode) || Program.isAppPlus(thisNode) || Program.isSsp(thisNode)) && ENTITY_APM.equalsIgnoreCase(entityType)) { wrapper.put(ENTITY_ID, thisNode.getValue(ENTITY_ID)); } diff --git a/converter/src/main/java/gov/cms/qpp/conversion/model/Constants.java b/converter/src/main/java/gov/cms/qpp/conversion/model/Constants.java index 4b1bbe3a2..c0c9fe694 100644 --- a/converter/src/main/java/gov/cms/qpp/conversion/model/Constants.java +++ b/converter/src/main/java/gov/cms/qpp/conversion/model/Constants.java @@ -67,6 +67,7 @@ public class Constants { public static final String ENTITY_VIRTUAL_GROUP = "virtualGroup"; public static final String APP_PROGRAM_NAME = "app1"; public static final String APP_PLUS_PROGRAM_NAME = "appPlus"; + public static final String SSP_PROGRAM_NAME = "ssp"; public static final String MIPS = "MIPS"; public static final Set MVP_ENTITIES = Set.of(ENTITY_INDIVIDUAL, ENTITY_GROUP, ENTITY_SUBGROUP, ENTITY_APM); diff --git a/converter/src/main/java/gov/cms/qpp/conversion/model/Program.java b/converter/src/main/java/gov/cms/qpp/conversion/model/Program.java index 9989a1adf..24165d94b 100644 --- a/converter/src/main/java/gov/cms/qpp/conversion/model/Program.java +++ b/converter/src/main/java/gov/cms/qpp/conversion/model/Program.java @@ -17,6 +17,7 @@ public enum Program { PCF("PCF"), APP("MIPS_APP1_INDIV", "MIPS_APP1_GROUP", "MIPS_APP1_APMENTITY"), APP_PLUS("APP_PLUS_INDIV", "APP_PLUS_GROUP", "APP_PLUS_APMENTITY"), + SSP("SSP_PI_INDIV", "SSP_PI_GROUP", "SSP_PI_APMENTITY"), ALL; private final Set aliases; @@ -70,6 +71,16 @@ public static boolean isAppPlus(Node node) { return extractProgram(node) == Program.APP_PLUS; } + /** + * Checks if a node is using the ssp program + * + * @param node + * @return + */ + public static boolean isSsp(Node node) { + return extractProgram(node) == Program.SSP; + } + /** * Extracts a program type from a node * diff --git a/converter/src/main/java/gov/cms/qpp/conversion/validate/ClinicalDocumentValidator.java b/converter/src/main/java/gov/cms/qpp/conversion/validate/ClinicalDocumentValidator.java index 7e973649d..fbdae4db1 100644 --- a/converter/src/main/java/gov/cms/qpp/conversion/validate/ClinicalDocumentValidator.java +++ b/converter/src/main/java/gov/cms/qpp/conversion/validate/ClinicalDocumentValidator.java @@ -53,7 +53,7 @@ protected void performValidation(final Node node) { String entityType = Optional.ofNullable(node.getValue(ENTITY_TYPE)).orElse(""); forceCheckErrors(node).valueIn(ProblemCode.CLINICAL_DOCUMENT_INCORRECT_PROGRAM_NAME.format(VALID_PROGRAM_NAMES, programName), - PROGRAM_NAME, MIPS_PROGRAM_NAME, PCF, APP_PROGRAM_NAME, APP_PLUS_PROGRAM_NAME); + PROGRAM_NAME, MIPS_PROGRAM_NAME, PCF, APP_PROGRAM_NAME, APP_PLUS_PROGRAM_NAME, SSP_PROGRAM_NAME); if (ENTITY_VIRTUAL_GROUP.equals(entityType)) { forceCheckErrors(node).value(ProblemCode.VIRTUAL_GROUP_ID_REQUIRED, ENTITY_ID); diff --git a/converter/src/main/java/gov/cms/qpp/conversion/validate/SspClinicalDocumentValidator.java b/converter/src/main/java/gov/cms/qpp/conversion/validate/SspClinicalDocumentValidator.java new file mode 100644 index 000000000..662a2e60a --- /dev/null +++ b/converter/src/main/java/gov/cms/qpp/conversion/validate/SspClinicalDocumentValidator.java @@ -0,0 +1,35 @@ +package gov.cms.qpp.conversion.validate; + +import gov.cms.qpp.conversion.model.Node; +import gov.cms.qpp.conversion.model.Program; +import gov.cms.qpp.conversion.model.TemplateId; +import gov.cms.qpp.conversion.model.Validator; +import gov.cms.qpp.conversion.model.error.LocalizedProblem; +import gov.cms.qpp.conversion.model.error.ProblemCode; + +import java.util.Locale; + +import static gov.cms.qpp.conversion.model.Constants.CATEGORY; +import static gov.cms.qpp.conversion.model.Constants.PROGRAM_NAME; + +@Validator(value = TemplateId.CLINICAL_DOCUMENT, program = Program.SSP) +public class SspClinicalDocumentValidator extends NodeValidator { + private static final String ALLOWED_CATEGORY = "pi"; + + @Override + protected void performValidation(Node clinicalDocument) { + String programName = clinicalDocument.getValue(PROGRAM_NAME).toUpperCase(Locale.ROOT); + clinicalDocument + .getChildNodes(TemplateId.MEASURE_SECTION_V5) + .forEach(sectionNode -> { + String category = sectionNode.getValue(CATEGORY); + System.out.println("category: " + category); + if (category == null || !ALLOWED_CATEGORY.equalsIgnoreCase(category)) { + LocalizedProblem error = ProblemCode.SSP_PI_ONLY_MEASURE_CATEGORY + .format(programName, category == null ? "none" : category); + checkErrors(sectionNode) + .valueIn(error, CATEGORY, ALLOWED_CATEGORY); + } + }); + } +} \ No newline at end of file diff --git a/converter/src/test/java/gov/cms/qpp/conversion/model/ProgramTest.java b/converter/src/test/java/gov/cms/qpp/conversion/model/ProgramTest.java index 40a05705d..e743738d4 100644 --- a/converter/src/test/java/gov/cms/qpp/conversion/model/ProgramTest.java +++ b/converter/src/test/java/gov/cms/qpp/conversion/model/ProgramTest.java @@ -136,6 +136,41 @@ void testIsAppPlusAppEntityIsTrue() { assertThat(Program.isAppPlus(node)).isTrue(); } + @Test + void testIsSspIndividualIsTrue() { + Node node = new Node(); + node.putValue(RAW_PROGRAM_NAME, "SSP_PI_INDIV"); + assertThat(Program.isSsp(node)).isTrue(); + } + + @Test + void testIsSspGroupIsTrue() { + Node node = new Node(); + node.putValue(RAW_PROGRAM_NAME, "SSP_PI_GROUP"); + assertThat(Program.isSsp(node)).isTrue(); + } + + @Test + void testIsSspAppEntityIsTrue() { + Node node = new Node(); + node.putValue(RAW_PROGRAM_NAME, "SSP_PI_APMENTITY"); + assertThat(Program.isSsp(node)).isTrue(); + } + + @Test + void testIsSspReturnsFalseForMips() { + Node node = new Node(); + node.putValue(RAW_PROGRAM_NAME, "MIPS_INDIV"); + assertThat(Program.isSsp(node)).isFalse(); + } + + @Test + void testIsSspReturnsFalseForNullValue() { + Node node = new Node(); + node.putValue(RAW_PROGRAM_NAME, null); + assertThat(Program.isSsp(node)).isFalse(); + } + @Override public Class> getEnumType() { return Program.class; diff --git a/converter/src/test/java/gov/cms/qpp/conversion/validate/SspClinicalDocumentValidatorTest.java b/converter/src/test/java/gov/cms/qpp/conversion/validate/SspClinicalDocumentValidatorTest.java new file mode 100644 index 000000000..920453d98 --- /dev/null +++ b/converter/src/test/java/gov/cms/qpp/conversion/validate/SspClinicalDocumentValidatorTest.java @@ -0,0 +1,72 @@ +package gov.cms.qpp.conversion.validate; + +import static com.google.common.truth.Truth.assertWithMessage; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import gov.cms.qpp.conversion.model.Node; +import gov.cms.qpp.conversion.model.TemplateId; +import gov.cms.qpp.conversion.model.error.Detail; +import gov.cms.qpp.conversion.model.error.ProblemCode; +import gov.cms.qpp.conversion.model.error.correspondence.DetailsErrorEquals; + +class SspClinicalDocumentValidatorTest { + + private Node clinicalDocumentNode; + private Node measureSectionNode; + + @BeforeEach + void setUp() { + clinicalDocumentNode = new Node(TemplateId.CLINICAL_DOCUMENT); + clinicalDocumentNode.putValue("programName", "SSP"); + + measureSectionNode = new Node(TemplateId.MEASURE_SECTION_V5); + } + + @Test + void testValidCategory_NoErrors() { + measureSectionNode.putValue("category", "pi"); + clinicalDocumentNode.addChildNode(measureSectionNode); + + SspClinicalDocumentValidator validator = new SspClinicalDocumentValidator(); + List errors = validator.validateSingleNode(clinicalDocumentNode).getErrors(); + + assertWithMessage("There should be no errors for category 'pi'") + .that(errors).isEmpty(); + } + + @Test + void testNullCategory_ShouldError() { + // category is not set + clinicalDocumentNode.addChildNode(measureSectionNode); + + SspClinicalDocumentValidator validator = new SspClinicalDocumentValidator(); + List errors = validator.validateSingleNode(clinicalDocumentNode).getErrors(); + + assertWithMessage("Must report SSP_PI_ONLY_MEASURE_CATEGORY when category is null") + .that(errors) + .comparingElementsUsing(DetailsErrorEquals.INSTANCE) + .containsExactly( + ProblemCode.SSP_PI_ONLY_MEASURE_CATEGORY.format("SSP", "none") + ); + } + + @Test + void testInvalidCategory_ShouldError() { + measureSectionNode.putValue("category", "invalid"); + clinicalDocumentNode.addChildNode(measureSectionNode); + + SspClinicalDocumentValidator validator = new SspClinicalDocumentValidator(); + List errors = validator.validateSingleNode(clinicalDocumentNode).getErrors(); + + assertWithMessage("Must report SSP_PI_ONLY_MEASURE_CATEGORY when category is invalid") + .that(errors) + .comparingElementsUsing(DetailsErrorEquals.INSTANCE) + .containsExactly( + ProblemCode.SSP_PI_ONLY_MEASURE_CATEGORY.format("SSP", "invalid") + ); + } +} diff --git a/converter/src/test/resources/ssp/2025/ssp-ApmEntity-Qrda-III.xml b/converter/src/test/resources/ssp/2025/ssp-ApmEntity-Qrda-III.xml new file mode 100644 index 000000000..00c7d9233 --- /dev/null +++ b/converter/src/test/resources/ssp/2025/ssp-ApmEntity-Qrda-III.xml @@ -0,0 +1,382 @@ + + + + + + + + + + + 2017 Eligible Clinicians (EC) and Eligible Professionals (EP) Sample QRDA-III + + + + + + + + + + + + + + + + + + + Good Health Clinic + + + + + + + + + + + + + + + + + + + + + + + + + + + + Good Health Clinic + + + + + + + + + + + + + + + + + +
+ + + + + + Measure Section + + + + + + + +
Reporting Period: January 01, 2023 - December 31, 2023
+ + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
Query of the Prescription Drug Monitoring Program (PDMP)PI_EP_2
+ + + Measure Answer (Yes/No): Yes + + + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
e-PrescribingPI_EP_1
+ + + Denominator: 800 + + Numerator: 750 + + + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
Provide Patients Electronic Access to their Health InformationPI_PEA_1
+ + + Denominator: 800 + + Numerator: 600 + +
+ + + + + + + + + + + + + + + + Query of the Prescription Drug Monitoring Program (PDMP) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + e-Prescribing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Provide Patients Electronic Access to their Health + Information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
diff --git a/converter/src/test/resources/ssp/2025/ssp-Group-QRDA-III.xml b/converter/src/test/resources/ssp/2025/ssp-Group-QRDA-III.xml new file mode 100644 index 000000000..9abb8fd14 --- /dev/null +++ b/converter/src/test/resources/ssp/2025/ssp-Group-QRDA-III.xml @@ -0,0 +1,395 @@ + + + + + + + + + + + 2017 Eligible Clinicians (EC) and Eligible Professionals (EP) Sample QRDA-III + + + + + + + + + + + + + + + + + + + Good Health Clinic + + + + + + + + + + + + + + + + + + + + + + + + + + + + Good Health Clinic + + + + + + + + + + Good Healthcare Practice + + + + + + + + + + + + + + + + + +
+ + + + + + Measure Section + + + + + + + +
Reporting Period: January 01, 2023 - December 31, 2023
+ + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
Query of the Prescription Drug Monitoring Program (PDMP)PI_EP_2
+ + + Measure Answer (Yes/No): Yes + + + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
e-PrescribingPI_EP_1
+ + + Denominator: 800 + + Numerator: 750 + + + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
Provide Patients Electronic Access to their Health InformationPI_PEA_1
+ + + Denominator: 800 + + Numerator: 600 + +
+ + + + + + + + + + + + + + + + Query of the Prescription Drug Monitoring Program (PDMP) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + e-Prescribing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Provide Patients Electronic Access to their Health + Information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
diff --git a/converter/src/test/resources/ssp/2025/ssp-Indv-QRDA-III.xml b/converter/src/test/resources/ssp/2025/ssp-Indv-QRDA-III.xml new file mode 100644 index 000000000..b0ef4e2bd --- /dev/null +++ b/converter/src/test/resources/ssp/2025/ssp-Indv-QRDA-III.xml @@ -0,0 +1,395 @@ + + + + + + + + + + + 2017 Eligible Clinicians (EC) and Eligible Professionals (EP) Sample QRDA-III + + + + + + + + + + + + + + + + + + + Good Health Clinic + + + + + + + + + + + + + + + + + + + + + + + + + + + + Good Health Clinic + + + + + + + + + + Good Healthcare Practice + + + + + + + + + + + + + + + + + +
+ + + + + + Measure Section + + + + + + + +
Reporting Period: January 01, 2023 - December 31, 2023
+ + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
Query of the Prescription Drug Monitoring Program (PDMP)PI_EP_2
+ + + Measure Answer (Yes/No): Yes + + + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
e-PrescribingPI_EP_1
+ + + Denominator: 800 + + Numerator: 750 + + + + + + + + + + + + + + +
PI Measure TitleMeasure Identifier
Provide Patients Electronic Access to their Health InformationPI_PEA_1
+ + + Denominator: 800 + + Numerator: 600 + +
+ + + + + + + + + + + + + + + + Query of the Prescription Drug Monitoring Program (PDMP) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + e-Prescribing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Provide Patients Electronic Access to their Health + Information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
diff --git a/generate-race-cpcplus/pom.xml b/generate-race-cpcplus/pom.xml index 1b67406b5..db3824319 100644 --- a/generate-race-cpcplus/pom.xml +++ b/generate-race-cpcplus/pom.xml @@ -5,7 +5,7 @@ qpp-conversion-tool-parent gov.cms.qpp.conversion - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../ 4.0.0 diff --git a/generate/pom.xml b/generate/pom.xml index 895bf19f4..8adae6155 100644 --- a/generate/pom.xml +++ b/generate/pom.xml @@ -5,7 +5,7 @@ qpp-conversion-tool-parent gov.cms.qpp.conversion - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../pom.xml 4.0.0 diff --git a/pom.xml b/pom.xml index f11afd43e..36e5acfad 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ gov.cms.qpp.conversion qpp-conversion-tool-parent pom - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE QPP Conversion Tool @@ -425,7 +425,13 @@ org.apache.commons commons-text - 1.10.0 + 1.13.1 + + + + org.apache.commons + commons-lang3 + 3.18.0 diff --git a/qrda3-update-measures/pom.xml b/qrda3-update-measures/pom.xml index 22611482d..4edbfd33a 100644 --- a/qrda3-update-measures/pom.xml +++ b/qrda3-update-measures/pom.xml @@ -3,7 +3,7 @@ qpp-conversion-tool-parent gov.cms.qpp.conversion - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../ diff --git a/rest-api/pom.xml b/rest-api/pom.xml index 9f43f757b..534f4b59e 100644 --- a/rest-api/pom.xml +++ b/rest-api/pom.xml @@ -6,7 +6,7 @@ gov.cms.qpp.conversion qpp-conversion-tool-parent - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../pom.xml @@ -18,7 +18,7 @@ 0.90 - 10.1.42 + 11.0.9 ${project.basedir}/../test-coverage/target/site/jacoco-aggregate/jacoco.xml 4.1.2 5.7.0 diff --git a/test-commons/pom.xml b/test-commons/pom.xml index 578a3a71b..511087c14 100644 --- a/test-commons/pom.xml +++ b/test-commons/pom.xml @@ -6,7 +6,7 @@ gov.cms.qpp.conversion qpp-conversion-tool-parent - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../pom.xml diff --git a/test-coverage/pom.xml b/test-coverage/pom.xml index 1c73c213f..d74150f83 100644 --- a/test-coverage/pom.xml +++ b/test-coverage/pom.xml @@ -6,7 +6,7 @@ gov.cms.qpp.conversion qpp-conversion-tool-parent - 2025.07.16.01-RELEASE + 2025.07.31.01-RELEASE ../pom.xml