diff --git a/services/app-web/src/gqlHelpers/contractsAndRates.ts b/services/app-web/src/gqlHelpers/contractsAndRates.ts index 7f8c97b3e2..b7a2a7a08f 100644 --- a/services/app-web/src/gqlHelpers/contractsAndRates.ts +++ b/services/app-web/src/gqlHelpers/contractsAndRates.ts @@ -1,14 +1,57 @@ /* These helpers help you access nested data from the Contract and Rate Apollo Client types -If the data doesn't exist, returs undefined reliably +If the data doesn't exist, returns undefined reliably */ -import { Contract, ContractFormData, ContractPackageSubmission, Rate } from "../gen/gqlClient" +import { Contract, ContractFormData, ContractPackageSubmission, Rate, RateRevision } from "../gen/gqlClient" + + +function getVisibleLatestRateRevisions(contract: Contract, isEditing: boolean): RateRevision[] | undefined { + if (isEditing) { + if (!contract.draftRates) { + console.error('Programming Error: on the rate details page with no draft rates') + return undefined + } + const rateRevs = [] + for (const rate of contract.draftRates) { + // if this is a child rate, return draft revision + if (rate.parentContractID === contract.id) { + if (!rate.draftRevision) { + // TODO: this error will likely no longer apply once we have Unlock/Submit Rate + // child rates will no longer be assumed to be unlocked with their parent contracts + console.error('Programming Error: A child rate is not a draft') + return undefined + } + rateRevs.push(rate.draftRevision) + } else { + // otherwise return the latest revision submitted. + const lastRateSubmission = rate.revisions[0] + if (!lastRateSubmission) { + console.error('Programming Error: non-child rate was not previously submitted') + return undefined + } + rateRevs.push(lastRateSubmission) + } + } + return rateRevs + } else { + const lastContractSubmission = getLastContractSubmission(contract) + if (!lastContractSubmission) { + console.error('Programming Error: no contract submission for a contract were not editing') + return undefined + } + return lastContractSubmission.rateRevisions + } +} // returns draft form data for unlocked and draft, and last package submission data for submitted or resubmitted -const getLatestContractFormData = (contract: Contract): ContractFormData | undefined =>{ - return contract.draftRevision?.formData || +// only state users get to see draft data. +const getVisibleLatestContractFormData = (contract: Contract, isStateUser: boolean): ContractFormData | undefined =>{ + if (isStateUser) { + return contract.draftRevision?.formData || getLastContractSubmission(contract)?.contractRevision.formData + } + return getLastContractSubmission(contract)?.contractRevision.formData } const getLastContractSubmission = (contract: Contract): ContractPackageSubmission | undefined => { @@ -19,4 +62,4 @@ const getDraftRates = (contract: Contract): Rate[] | undefined => { return (contract.draftRates && contract.draftRates[0]) ? contract.draftRates : undefined } -export {getDraftRates, getLastContractSubmission, getLatestContractFormData} \ No newline at end of file +export {getDraftRates, getLastContractSubmission, getVisibleLatestContractFormData, getVisibleLatestRateRevisions} diff --git a/services/app-web/src/pages/LinkYourRates/LinkRateSelect.tsx b/services/app-web/src/pages/LinkYourRates/LinkRateSelect.tsx index 7ef571d3a2..423e8c8c73 100644 --- a/services/app-web/src/pages/LinkYourRates/LinkRateSelect.tsx +++ b/services/app-web/src/pages/LinkYourRates/LinkRateSelect.tsx @@ -113,7 +113,6 @@ export const LinkRateSelect = ({ ) => { if (action === 'select-option') { const linkedRateID = newValue.value - // const linkedRateName = newValue.label const linkedRate = rates.find((rate) => rate.id === linkedRateID) const linkedRateForm: FormikRateForm = convertGQLRateToRateForm( getKey, diff --git a/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.test.tsx b/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.test.tsx index a62ffb5d85..6b703e6b41 100644 --- a/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.test.tsx +++ b/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.test.tsx @@ -300,7 +300,7 @@ describe('RateDetailsv2', () => { }) it('display rest of the form when linked rates question is answered', async () => { - renderWithProviders( + const { user } = renderWithProviders( { contract: { ...mockContractWithLinkedRateDraft(), id: 'test-abc-123', - }, - }), - updateDraftContractRatesMockSuccess({ - contract: { - id: 'test-abc-123', + // clean draft rates for this test. + draftRates: [] }, }), ], @@ -340,16 +337,17 @@ describe('RateDetailsv2', () => { 'No, this rate certification was not included with any other submissions' ) ) + const input = screen.getByLabelText( 'Upload one rate certification document' ) - await expect(input).toBeInTheDocument() + expect(input).toBeInTheDocument() const submitButton = screen.getByRole('button', { name: 'Continue', }) // trigger validations - await submitButton.click() + await user.click(submitButton) await waitFor(() => { expect( screen.getByText('Rate certification 1') @@ -458,7 +456,7 @@ describe('RateDetailsv2', () => { }) }) it('cannot continue with partially filled out second rate', async () => { - renderWithProviders( + const { user } = renderWithProviders( { await screen.findByText('Rate Details') const rateCertsOnLoad = rateCertifications(screen) expect(rateCertsOnLoad).toHaveLength(1) - await fillOutIndexRate(screen, 0) await clickAddNewRate(screen) const submitButton = screen.getByRole('button', { @@ -503,7 +500,7 @@ describe('RateDetailsv2', () => { }) // trigger validations - await submitButton.click() + await user.click(submitButton) await waitFor(() => { expect( screen.getByText('Rate certification 1') diff --git a/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.tsx b/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.tsx index 782e3dc8b8..4ad6961740 100644 --- a/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.tsx +++ b/services/app-web/src/pages/StateSubmission/RateDetails/V2/RateDetailsV2.tsx @@ -164,8 +164,8 @@ const RateDetailsV2 = ({ const [submitRate, { error: submitRateError }] = useSubmitRateMutation() // Set up data for form. Either based on contract API (for multi rate) or rates API (for edit and submit of standalone rate) - const ratesFromContract = - fetchContractData?.fetchContract.contract.draftRates + const contract = fetchContractData?.fetchContract.contract + const ratesFromContract = contract?.draftRates const initialRequestLoading = fetchContractLoading || fetchRateLoading const initialRequestError = fetchContractError || fetchRateError const submitRequestError = updateContractError || submitRateError @@ -188,7 +188,7 @@ const RateDetailsV2 = ({ rateForms: initialRates.length > 0 ? initialRates.map((rate) => - convertGQLRateToRateForm(getKey, rate) + convertGQLRateToRateForm(getKey, rate, contract?.id) ) : [convertGQLRateToRateForm(getKey)], } diff --git a/services/app-web/src/pages/StateSubmission/RateDetails/V2/rateDetailsHelpers.ts b/services/app-web/src/pages/StateSubmission/RateDetails/V2/rateDetailsHelpers.ts index 89d76e2493..dfcfb6f15a 100644 --- a/services/app-web/src/pages/StateSubmission/RateDetails/V2/rateDetailsHelpers.ts +++ b/services/app-web/src/pages/StateSubmission/RateDetails/V2/rateDetailsHelpers.ts @@ -64,8 +64,8 @@ const convertRateFormToGQLRateFormData = ( // Convert from GQL Rate to FormikRateForm object used in the form // if rate is not passed in, return an empty RateForm // we need to pass in the s3 handler because 3 urls generated client-side // useLatestSubmission means to pull the latest submitted info rather than the draft info -const convertGQLRateToRateForm = (getKey: S3ClientT['getKey'], rate?: Rate): FormikRateForm => { - const handleAsLinkedRate = rate?.status && rate.status !== 'DRAFT' && rate.status !== 'UNLOCKED' // TODO: Make this a more sophisticated check for child-rates +const convertGQLRateToRateForm = (getKey: S3ClientT['getKey'], rate?: Rate, parentContractID?: string): FormikRateForm => { + const handleAsLinkedRate = rate && rate.parentContractID !== parentContractID // TODO: Make this a more sophisticated check for child-rates const rateRev = handleAsLinkedRate ? rate?.revisions[0] : rate?.draftRevision const rateForm = rateRev?.formData return { diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.test.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.test.tsx index 3b20f565b4..3816081eaa 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.test.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.test.tsx @@ -219,6 +219,7 @@ describe('ContractDetailsSummarySection', () => { , { apolloProvider: defaultApolloMocks, @@ -268,6 +269,7 @@ describe('ContractDetailsSummarySection', () => { , { apolloProvider: defaultApolloMocks, @@ -365,6 +367,7 @@ describe('ContractDetailsSummarySection', () => { , { apolloProvider: defaultApolloMocks, @@ -408,6 +411,7 @@ describe('ContractDetailsSummarySection', () => { , { apolloProvider: defaultApolloMocks, diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.tsx index 81a5f58f2e..7e8fbaf809 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ContractDetailsSummarySectionV2.tsx @@ -45,7 +45,7 @@ import { SectionCard } from '../../../../../components/SectionCard' import { Contract } from '../../../../../gen/gqlClient' import { getLastContractSubmission, - getLatestContractFormData, + getVisibleLatestContractFormData, } from '../../../../../gqlHelpers/contractsAndRates' export type ContractDetailsSummarySectionV2Props = { @@ -84,8 +84,9 @@ export const ContractDetailsSummarySectionV2 = ({ string | undefined | Error >(undefined) const ldClient = useLDClient() + const isEditing = !isSubmitted(contract) && editNavigateTo !== undefined - const contractFormData = getLatestContractFormData(contract) + const contractFormData = getVisibleLatestContractFormData(contract, isEditing) const contract438Attestation = ldClient?.variation( featureFlags.CONTRACT_438_ATTESTATION.flag, featureFlags.CONTRACT_438_ATTESTATION.defaultValue @@ -96,7 +97,6 @@ export const ContractDetailsSummarySectionV2 = ({ booleanAsYesNoFormValue(contractFormData.statutoryRegulatoryAttestation) const contractSupportingDocuments = contractFormData?.supportingDocuments - const isEditing = !isSubmitted(contract) && editNavigateTo !== undefined const applicableFederalAuthorities = isCHIPOnly(contract) ? contractFormData?.federalAuthorities.filter((authority) => federalAuthorityKeysForCHIP.includes( diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.test.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.test.tsx index 37cbee1f3b..51ff032cd0 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.test.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.test.tsx @@ -25,7 +25,7 @@ describe('RateDetailsSummarySection', () => { state: mockMNState(), stateCode: 'MN', stateNumber: 5, - parentContractID: 'fake-id', + parentContractID: 'test-abc-123', revisions: [], draftRevision: { id: '1234', @@ -74,7 +74,7 @@ describe('RateDetailsSummarySection', () => { state: mockMNState(), stateCode: 'MN', stateNumber: 5, - parentContractID: 'fake-id', + parentContractID: 'test-abc-123', revisions: [], draftRevision: { id: '1234', diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.tsx index 9fc317f6c2..a8da1ee62b 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/RateDetailsSummarySectionV2.tsx @@ -24,7 +24,7 @@ import { RateFormData, HealthPlanPackageStatus, } from '../../../../../gen/gqlClient' -import { getLastContractSubmission } from '../../../../../gqlHelpers/contractsAndRates' +import { getLastContractSubmission, getVisibleLatestRateRevisions } from '../../../../../gqlHelpers/contractsAndRates' export type RateDetailsSummarySectionV2Props = { contract: Contract @@ -74,12 +74,10 @@ export const RateDetailsSummarySectionV2 = ({ const isSubmitted = contract.status === 'SUBMITTED' const isEditing = !isSubmitted && editNavigateTo !== undefined const isPreviousSubmission = usePreviousSubmission() - const contractFormData = - contract.draftRevision?.formData || + const contractFormData = isEditing ? + contract.draftRevision?.formData : getLastContractSubmission(contract)?.contractRevision.formData - const rates = isEditing - ? contract.draftRates - : getLastContractSubmission(contract)?.rateRevisions + const rates = getVisibleLatestRateRevisions(contract, isEditing) const lastSubmittedDate = getLastContractSubmission(contract)?.submitInfo.updatedAt @@ -224,6 +222,7 @@ export const RateDetailsSummarySectionV2 = ({ isSubmitted, isPreviousSubmission, ]) + return ( { it('renders without errors', async () => { @@ -175,7 +177,7 @@ describe('ReviewSubmit', () => { }) it('displays back, save as draft, and submit buttons', async () => { - renderWithProviders( + const { user } = renderWithProviders( { expect(screen.getByTestId('form-submit')).toBeDefined() expect(screen.getAllByText('Submit')).toHaveLength(2) - await screen.getAllByText('Submit')[0].click() + await user.click(screen.getAllByText('Submit')[0]) + }) + + it('pulls the right version of UNLOCKED data for state users', async () => { + renderWithProviders( + + } + /> + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + statusCode: 200, + user: mockValidStateUser(), + }), + fetchContractMockSuccess({ + contract: mockContractPackageUnlocked(), + }), + ], + }, + routerProvider: { + route: '/submissions/test-abc-123/edit/review-and-submit', + }, + featureFlags: { + 'link-rates': true, + }, + } + ) + + const description = await screen.findByLabelText('Submission description') + expect(description).toHaveTextContent('An updated submission') + const ratingPeriod = await screen.findByLabelText('Rating period of original rate certification') + expect(ratingPeriod).toHaveTextContent('02/02/2020 to 02/02/2021') + }) }) diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ReviewSubmitV2.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ReviewSubmitV2.tsx index 06b8de606c..5e256c9d3d 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ReviewSubmitV2.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/ReviewSubmitV2.tsx @@ -15,7 +15,7 @@ import { STATE_SUBMISSION_FORM_ROUTES, } from '../../../../../constants' import { UnlockSubmitModalV2 } from '../../../../../components/Modal/V2/UnlockSubmitModalV2' -import { getLatestContractFormData } from '../../../../../gqlHelpers/contractsAndRates' +import { getVisibleLatestContractFormData } from '../../../../../gqlHelpers/contractsAndRates' import { useAuth } from '../../../../../contexts/AuthContext' import { RateDetailsSummarySectionV2 } from './RateDetailsSummarySectionV2' import { ContactsSummarySection } from './ContactsSummarySectionV2' @@ -75,7 +75,8 @@ export const ReviewSubmitV2 = (): React.ReactElement => { } } - const contractFormData = getLatestContractFormData(contract) + const isStateUser = loggedInUser?.role === 'STATE_USER' + const contractFormData = getVisibleLatestContractFormData(contract, isStateUser) if (!contractFormData) return const isContractActionAndRateCertification = @@ -113,6 +114,7 @@ export const ReviewSubmitV2 = (): React.ReactElement => { submissionName={submissionName} editNavigateTo="../type" statePrograms={statePrograms} + isStateUser={isStateUser} /> { statePrograms={statePrograms} editNavigateTo="submission-type" submissionName="MN-PMAP-0001" + isStateUser={true} /> ) @@ -48,6 +49,7 @@ describe('SubmissionTypeSummarySection', () => { contract={stateSubmission} statePrograms={statePrograms} submissionName="MN-MSHO-0003" + isStateUser={true} /> ) @@ -71,6 +73,7 @@ describe('SubmissionTypeSummarySection', () => { statePrograms={statePrograms} editNavigateTo="submission-type" submissionName="MN-PMAP-0001" + isStateUser={true} /> ) @@ -109,6 +112,7 @@ describe('SubmissionTypeSummarySection', () => { statePrograms={statePrograms} editNavigateTo="submission-type" submissionName="MN-PMAP-0001" + isStateUser={true} /> ) } @@ -137,6 +141,7 @@ describe('SubmissionTypeSummarySection', () => { statePrograms={statePrograms} editNavigateTo="submission-type" submissionName="MN-PMAP-0001" + isStateUser={true} /> ) } @@ -164,6 +169,7 @@ describe('SubmissionTypeSummarySection', () => { statePrograms={statePrograms} editNavigateTo="submission-type" submissionName="MN-MSHO-0003" + isStateUser={true} /> ) expect( @@ -187,6 +193,7 @@ describe('SubmissionTypeSummarySection', () => { statePrograms={statePrograms} editNavigateTo="submission-type" submissionName="MN-PMAP-0001" + isStateUser={true} /> ) expect( @@ -202,6 +209,7 @@ describe('SubmissionTypeSummarySection', () => { editNavigateTo="submission-type" headerChildComponent={} submissionName="MN-PMAP-0001" + isStateUser={true} /> ) expect( diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/SubmissionTypeSummarySectionV2.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/SubmissionTypeSummarySectionV2.tsx index bf31fc0259..83f7db7ba8 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/SubmissionTypeSummarySectionV2.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/V2/ReviewSubmit/SubmissionTypeSummarySectionV2.tsx @@ -9,7 +9,7 @@ import { PopulationCoveredRecord, } from '../../../../../constants/healthPlanPackages' import { GenericErrorPage } from '../../../../Errors/GenericErrorPage' -import { getLatestContractFormData } from '../../../../../gqlHelpers/contractsAndRates' +import { getVisibleLatestContractFormData } from '../../../../../gqlHelpers/contractsAndRates' import { Program, Contract } from '../../../../../gen/gqlClient' import { usePreviousSubmission } from '../../../../../hooks/usePreviousSubmission' import { booleanAsYesNoUserValue } from '../../../../../components/Form/FieldYesNo/FieldYesNo' @@ -24,6 +24,7 @@ export type SubmissionTypeSummarySectionV2Props = { subHeaderComponent?: React.ReactElement initiallySubmittedAt?: Date submissionName: string + isStateUser: boolean } export const SubmissionTypeSummarySectionV2 = ({ @@ -34,9 +35,10 @@ export const SubmissionTypeSummarySectionV2 = ({ headerChildComponent, initiallySubmittedAt, submissionName, + isStateUser, }: SubmissionTypeSummarySectionV2Props): React.ReactElement => { const isPreviousSubmission = usePreviousSubmission() - const contractFormData = getLatestContractFormData(contract) + const contractFormData = getVisibleLatestContractFormData(contract, isStateUser) if (!contractFormData) return const programNames = statePrograms diff --git a/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.test.tsx b/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.test.tsx index 7a0bd97e50..12d1dd0850 100644 --- a/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.test.tsx +++ b/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.test.tsx @@ -1,4 +1,3 @@ -import fs from 'fs' import { screen, waitFor, within } from '@testing-library/react' import { Route, Routes } from 'react-router' import { domainToBase64 } from '../../../common-code/proto/healthPlanFormDataProto' @@ -11,7 +10,6 @@ import { mockValidUser, mockValidStateUser, mockUnlockedHealthPlanPackage, - mockContractPackageDraft, mockContractPackageSubmitted, } from '../../../testHelpers/apolloMocks' import { basicLockedHealthPlanFormData } from '../../../common-code/healthPlanFormDataMocks' @@ -19,6 +17,7 @@ import { renderWithProviders } from '../../../testHelpers/jestHelpers' import { SubmissionSummaryV2 } from './SubmissionSummaryV2' import { SubmissionSideNav } from '../../SubmissionSideNav' import { testS3Client } from '../../../testHelpers/s3Helpers' +import { mockContractPackageUnlocked } from '../../../testHelpers/apolloMocks/contractPackageDataMock' describe('SubmissionSummary', () => { it('renders without errors', async () => { @@ -163,6 +162,49 @@ describe('SubmissionSummary', () => { // }) // }) + it('pulls the right version of UNLOCKED data for CMS users', async () => { + renderWithProviders( + + }> + } + /> + + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + statusCode: 200, + user: mockValidCMSUser(), + }), + fetchContractMockSuccess({ + contract: mockContractPackageUnlocked(), + }), + fetchStateHealthPlanPackageWithQuestionsMockSuccess({ + id: 'test-abc-123', + }), + ], + }, + routerProvider: { + route: '/submissions/test-abc-123', + }, + featureFlags: { + 'link-rates': true, + }, + } + ) + + expect(await screen.findByText('MCR-MN-0005-SNBC')).toBeInTheDocument() + + const description = await screen.findByLabelText('Submission description') + expect(description).toHaveTextContent('An initial submission') + const ratingPeriod = await screen.findByLabelText('Rating period of original rate certification') + expect(ratingPeriod).toHaveTextContent('01/01/2020 to 01/01/2021') + + }) + it('renders add mccrs-id link for CMS user', async () => { renderWithProviders( @@ -181,9 +223,9 @@ describe('SubmissionSummary', () => { user: mockValidCMSUser(), }), fetchContractMockSuccess({ - contract: { + contract: mockContractPackageSubmitted({ id: 'test-abc-123', - }, + }), }), fetchStateHealthPlanPackageWithQuestionsMockSuccess({ id: 'test-abc-123', @@ -223,10 +265,10 @@ describe('SubmissionSummary', () => { statusCode: 200, }), fetchContractMockSuccess({ - contract: { + contract: mockContractPackageSubmitted({ id: 'test-abc-123', mccrsID: '3333', - }, + }), }), fetchStateHealthPlanPackageWithQuestionsMockSuccess({ id: 'test-abc-123', @@ -349,8 +391,8 @@ describe('SubmissionSummary', () => { const s3Provider = { ...testS3Client(), getBulkDlURL: async ( - keys: string[], - fileName: string + _keys: string[], + _fileName: string ): Promise => { return new Error('Error: getBulkDlURL encountered an error') }, @@ -466,9 +508,7 @@ describe('SubmissionSummary', () => { id: 'test-abc-123', }), fetchContractMockSuccess({ - contract: { - id: 'test-abc-123', - }, + contract: mockContractPackageUnlocked(), }), ], }, @@ -654,9 +694,7 @@ describe('SubmissionSummary', () => { } ), fetchContractMockSuccess({ - contract: { - id: 'test-abc-123', - }, + contract: mockContractPackageSubmitted(), }), ], }, diff --git a/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.tsx b/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.tsx index 0bbcd1d6c5..70b69cd175 100644 --- a/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.tsx +++ b/services/app-web/src/pages/SubmissionSummary/V2/SubmissionSummaryV2.tsx @@ -34,6 +34,7 @@ import { useLDClient } from 'launchdarkly-react-client-sdk' import { featureFlags } from '../../../common-code/featureFlags' import { RoutesRecord } from '../../../constants' import { useRouteParams } from '../../../hooks' +import { getVisibleLatestContractFormData } from '../../../gqlHelpers/contractsAndRates' function UnlockModalButton({ disabled, @@ -115,11 +116,16 @@ export const SubmissionSummaryV2 = (): React.ReactElement => { } } const isCMSUser = loggedInUser?.role === 'CMS_USER' + const isStateUser = loggedInUser?.role === 'STATE_USER' const submissionStatus = contract.status const statePrograms = contract.state.programs - const contractFormData = - contract.draftRevision?.formData || - contract.packageSubmissions[0].contractRevision.formData + + const contractFormData = getVisibleLatestContractFormData(contract, isStateUser) + if (!contractFormData) { + console.error('no form data inside submission summary') + return + } + const programIDs = contractFormData.programIDs const programs = statePrograms.filter((program) => programIDs.includes(program.id) @@ -253,6 +259,7 @@ export const SubmissionSummaryV2 = (): React.ReactElement => { } statePrograms={statePrograms} initiallySubmittedAt={contract.initiallySubmittedAt} + isStateUser={isStateUser} /> )} diff --git a/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts b/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts index bc7166e547..3f563dbd03 100644 --- a/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts +++ b/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts @@ -83,7 +83,7 @@ function mockContractPackageDraft( revisions: [], state: mockMNState(), stateNumber: 5, - parentContractID: 'foo-baz', + parentContractID: 'test-abc-123', draftRevision: { id: '123', rateID: '456', @@ -207,22 +207,24 @@ function mockContractWithLinkedRateDraft( }, draftRates: [ + // a linked, unlocked, rate. { - id: '123', + id: 'rate-123', createdAt: new Date(), updatedAt: new Date(), - status: 'SUBMITTED', + status: 'UNLOCKED', stateCode: 'MN', - revisions: [], state: mockMNState(), stateNumber: 5, - parentContractID: 'foo-baz', + parentContractID: 'some-other-contract-id', draftRevision: { id: '123', - rateID: '456', + rateID: 'rate-123', contractRevisions: [], createdAt: new Date(), updatedAt: new Date(), + submitInfo: undefined, + unlockInfo: undefined, formData: { rateType: 'AMENDMENT', rateCapitationType: 'RATE_CELL', @@ -235,8 +237,8 @@ function mockContractWithLinkedRateDraft( }, ], supportingDocuments: [], - rateDateStart: new Date(), - rateDateEnd: new Date(), + rateDateStart: new Date('2020-02-02'), + rateDateEnd: new Date('2021-02-02'), rateDateCertified: new Date(), amendmentEffectiveDateStart: new Date(), amendmentEffectiveDateEnd: new Date(), @@ -260,8 +262,53 @@ function mockContractWithLinkedRateDraft( actuaryCommunicationPreference: 'OACT_TO_ACTUARY', packagesWithSharedRateCerts: [], } - } - + }, + revisions: [{ + id: '456', + rateID: 'rate-123', + contractRevisions: [], + createdAt: new Date(), + updatedAt: new Date(), + submitInfo: undefined, + unlockInfo: undefined, + formData: { + rateType: 'AMENDMENT', + rateCapitationType: 'RATE_CELL', + rateDocuments: [ + { + s3URL: 's3://bucketname/key/rate', + sha256: 'fakesha', + name: 'rate', + dateAdded: new Date() + }, + ], + supportingDocuments: [], + rateDateStart: new Date('2020-01-01'), + rateDateEnd: new Date('2021-01-01'), + rateDateCertified: new Date(), + amendmentEffectiveDateStart: new Date(), + amendmentEffectiveDateEnd: new Date(), + rateProgramIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'], + certifyingActuaryContacts: [ + { + actuarialFirm: 'DELOITTE', + name: 'Actuary Contact 1', + titleRole: 'Test Actuary Contact 1', + email: 'actuarycontact1@test.com', + }, + ], + addtlActuaryContacts: [ + { + actuarialFirm: 'DELOITTE', + name: 'Actuary Contact 1', + titleRole: 'Test Actuary Contact 1', + email: 'additionalactuarycontact1@test.com', + }, + ], + actuaryCommunicationPreference: 'OACT_TO_ACTUARY', + packagesWithSharedRateCerts: [], + } + }] }, ], packageSubmissions: [], @@ -291,6 +338,11 @@ function mockContractPackageSubmitted( contractName: 'MCR-MN-0005-SNBC', createdAt: new Date('01/01/2024'), updatedAt: new Date('12/31/2024'), + submitInfo: { + updatedAt: new Date(), + updatedBy: 'example@state.com', + updatedReason: 'contract submit' + }, id: '123', formData: { programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'], @@ -341,6 +393,11 @@ function mockContractPackageSubmitted( rateID: '456', createdAt: new Date('01/01/2023'), updatedAt: new Date('01/01/2023'), + submitInfo: { + updatedAt: new Date(), + updatedBy: 'example@state.com', + updatedReason: 'contract submit' + }, contractRevisions: [], formData: { rateType: 'AMENDMENT', @@ -386,4 +443,257 @@ function mockContractPackageSubmitted( } } -export { mockContractPackageDraft, mockContractPackageSubmitted, mockContractWithLinkedRateDraft } \ No newline at end of file +function mockContractPackageUnlocked( + partial?: Partial +): Contract { + return { + status: 'UNLOCKED', + createdAt: new Date(), + updatedAt: new Date(), + id: 'test-abc-123', + stateCode: 'MN', + state: mockMNState(), + stateNumber: 5, + draftRevision: { + __typename: 'ContractRevision', + submitInfo: undefined, + unlockInfo: { + updatedAt: new Date(), + updatedBy: 'soandso@example.com', + updatedReason: 'unlocked for a test', + }, + id: '123', + createdAt: new Date(), + updatedAt: new Date(), + contractName: 'MCR-MN-0005-SNBC', + formData: { + programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'], + populationCovered: 'MEDICAID', + submissionType: 'CONTRACT_AND_RATES', + riskBasedContract: true, + submissionDescription: 'An updated submission', + supportingDocuments: [], + stateContacts: [ + { + name: 'State Contact 1', + titleRole: 'Test State Contact 1', + email: 'actuarycontact1@test.com', + }, + ], + contractType: 'AMENDMENT', + contractExecutionStatus: 'EXECUTED', + contractDocuments: [ + { + s3URL: 's3://bucketname/one-two/one-two.png', + sha256: 'fakesha', + name: 'one two', + dateAdded: new Date() + }, + ], + contractDateStart: new Date('02/02/2023'), + contractDateEnd: new Date('02/02/2024'), + managedCareEntities: ['MCO'], + federalAuthorities: ['STATE_PLAN'], + inLieuServicesAndSettings: true, + modifiedBenefitsProvided: true, + modifiedGeoAreaServed: false, + modifiedMedicaidBeneficiaries: true, + modifiedRiskSharingStrategy: true, + modifiedIncentiveArrangements: false, + modifiedWitholdAgreements: false, + modifiedStateDirectedPayments: true, + modifiedPassThroughPayments: true, + modifiedPaymentsForMentalDiseaseInstitutions: false, + modifiedMedicalLossRatioStandards: true, + modifiedOtherFinancialPaymentIncentive: false, + modifiedEnrollmentProcess: true, + modifiedGrevienceAndAppeal: false, + modifiedNetworkAdequacyStandards: true, + modifiedLengthOfContract: false, + modifiedNonRiskPaymentArrangements: true, + statutoryRegulatoryAttestation: true, + statutoryRegulatoryAttestationDescription: "everything meets regulatory attestation" + } + }, + + draftRates: [ + { + id: '123', + createdAt: new Date(), + updatedAt: new Date(), + status: 'SUBMITTED', + stateCode: 'MN', + revisions: [], + state: mockMNState(), + stateNumber: 5, + parentContractID: 'test-abc-123', + draftRevision: { + id: '123', + rateID: '456', + contractRevisions: [], + createdAt: new Date(), + updatedAt: new Date(), + unlockInfo: { + updatedAt: new Date(), + updatedBy: 'soandso@example.com', + updatedReason: 'unlocked for a test', + }, + formData: { + rateType: 'AMENDMENT', + rateCapitationType: 'RATE_CELL', + rateDocuments: [ + { + s3URL: 's3://bucketname/key/rate', + sha256: 'fakesha', + name: 'rate', + dateAdded: new Date() + }, + ], + supportingDocuments: [], + rateDateStart: new Date('2020-02-02'), + rateDateEnd: new Date('2021-02-02'), + rateDateCertified: new Date(), + amendmentEffectiveDateStart: new Date(), + amendmentEffectiveDateEnd: new Date(), + rateProgramIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'], + certifyingActuaryContacts: [ + { + actuarialFirm: 'DELOITTE', + name: 'Actuary Contact 1', + titleRole: 'Test Actuary Contact 1', + email: 'actuarycontact1@test.com', + }, + ], + addtlActuaryContacts: [ + { + actuarialFirm: 'DELOITTE', + name: 'Actuary Contact 1', + titleRole: 'Test Actuary Contact 1', + email: 'additionalactuarycontact1@test.com', + }, + ], + actuaryCommunicationPreference: 'OACT_TO_ACTUARY', + packagesWithSharedRateCerts: [], + } + } + + }, + ], + packageSubmissions: [{ + cause: 'CONTRACT_SUBMISSION', + submitInfo: { + updatedAt: new Date('01/01/2024'), + updatedBy: 'example@state.com', + updatedReason: 'initial submission' + }, + submittedRevisions: [], + contractRevision: { + contractName: 'MCR-MN-0005-SNBC', + createdAt: new Date('01/01/2024'), + updatedAt: new Date('12/31/2024'), + submitInfo: { + updatedAt: new Date('01/01/2024'), + updatedBy: 'example@state.com', + updatedReason: 'initial submission' + }, + id: '123', + formData: { + programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'], + populationCovered: 'MEDICAID', + submissionType: 'CONTRACT_AND_RATES', + riskBasedContract: true, + submissionDescription: 'An initial submission', + supportingDocuments: [], + stateContacts: [], + contractType: 'AMENDMENT', + contractExecutionStatus: 'EXECUTED', + contractDocuments: [ + { + s3URL: 's3://bucketname/key/contract', + sha256: 'fakesha', + name: 'contract', + dateAdded: new Date() + }, + ], + contractDateStart: new Date('01/01/2023'), + contractDateEnd: new Date('01/01/2024'), + managedCareEntities: ['MCO'], + federalAuthorities: ['STATE_PLAN'], + inLieuServicesAndSettings: true, + modifiedBenefitsProvided: true, + modifiedGeoAreaServed: false, + modifiedMedicaidBeneficiaries: true, + modifiedRiskSharingStrategy: true, + modifiedIncentiveArrangements: false, + modifiedWitholdAgreements: false, + modifiedStateDirectedPayments: true, + modifiedPassThroughPayments: true, + modifiedPaymentsForMentalDiseaseInstitutions: false, + modifiedMedicalLossRatioStandards: true, + modifiedOtherFinancialPaymentIncentive: false, + modifiedEnrollmentProcess: true, + modifiedGrevienceAndAppeal: false, + modifiedNetworkAdequacyStandards: true, + modifiedLengthOfContract: false, + modifiedNonRiskPaymentArrangements: true, + statutoryRegulatoryAttestation: true, + statutoryRegulatoryAttestationDescription: "everything meets regulatory attestation" + } + }, + rateRevisions: [ + { + id: '1234', + rateID: '456', + createdAt: new Date('01/01/2023'), + updatedAt: new Date('01/01/2023'), + submitInfo: { + updatedAt: new Date('01/01/2024'), + updatedBy: 'example@state.com', + updatedReason: 'initial submission' + }, + contractRevisions: [], + formData: { + rateType: 'AMENDMENT', + rateCapitationType: 'RATE_CELL', + rateDocuments: [ + { + s3URL: 's3://bucketname/key/rate', + sha256: 'fakesha', + name: 'rate', + dateAdded: new Date() + }, + ], + supportingDocuments: [], + rateDateStart: new Date('2020-01-01'), + rateDateEnd: new Date('2021-01-01'), + rateDateCertified: new Date(), + amendmentEffectiveDateStart: new Date(), + amendmentEffectiveDateEnd: new Date(), + rateProgramIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'], + certifyingActuaryContacts: [ + { + actuarialFirm: 'DELOITTE', + name: 'Actuary Contact 1', + titleRole: 'Test Actuary Contact 1', + email: 'actuarycontact1@test.com', + }, + ], + addtlActuaryContacts: [ + { + actuarialFirm: 'DELOITTE', + name: 'Actuary Contact 1', + titleRole: 'Test Actuary Contact 1', + email: 'actuarycontact1@test.com', + }, + ], + actuaryCommunicationPreference: 'OACT_TO_ACTUARY', + packagesWithSharedRateCerts: [] + } + }, + ], + }], + ...partial, + } +} + +export { mockContractPackageDraft, mockContractPackageSubmitted, mockContractWithLinkedRateDraft, mockContractPackageUnlocked }