Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MCR-2359 submit + unlock resolver #1903

Merged
merged 26 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dab3b07
submitContract working
haworku Aug 28, 2023
b70b14a
submitRate working - update change history docs
haworku Aug 29, 2023
02f72bf
Tests passing
haworku Sep 1, 2023
cc3cf2b
WIP - final commit - no more time to spend here
haworku Sep 1, 2023
d188e21
Add in tests feature flag, skip status tests - I'm confused what type…
haworku Sep 1, 2023
723a552
Add back status tests - remove calculated status in submit
haworku Sep 1, 2023
7135646
Tests compiling - working on emails
haworku Sep 8, 2023
2f8e8f1
Add submitRate
haworku Sep 11, 2023
13c5c25
Backtrack docs changes - added those to #1922 instead
haworku Sep 13, 2023
c1954ce
start unlock work
macrael Sep 15, 2023
3f71d30
Merge branch 'main' into mr-3447-submit
macrael Sep 22, 2023
193abf2
more shas
macrael Sep 22, 2023
9a6f3eb
dont force wip test to fail
macrael Sep 25, 2023
c12f338
test everywhere
macrael Sep 25, 2023
4e0040d
No unlock refactor tests for now
macrael Sep 25, 2023
293e811
might need this compliation step
macrael Sep 25, 2023
80054af
undo all dev tool work
macrael Sep 26, 2023
bf4e57a
Merge branch 'main' into mr-3447-submit
macrael Sep 26, 2023
8d0995b
everything else
macrael Sep 27, 2023
b41cee5
Merge branch 'wml-unlock-rates-refactor' into mr-3447-submit
macrael Sep 27, 2023
1783217
get app-web tests passing
macrael Sep 29, 2023
96f62b5
get all tests passing:
macrael Sep 29, 2023
a4899b9
address jason comments
macrael Sep 29, 2023
5794743
fix up submit parsing
macrael Oct 2, 2023
c4e7c3e
im adding the bad imports
macrael Oct 2, 2023
b6c4b35
address comments
macrael Oct 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions dev_tool/src/local/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { compileGraphQLTypesWatchOnce } from './graphql.js'
import { installPrismaDeps } from './postgres.js'

export async function installAPIDeps(runner: LabeledProcessRunner) {
await runner.runCommandAndOutput('api deps', ['yarn', 'install'], '')

// prisma requires that prisma generate is run after any yarn install
return installPrismaDeps(runner)
}
Expand Down
2 changes: 0 additions & 2 deletions dev_tool/src/local/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { once } from '../deps.js'

// run the graphql compiler with --watch
async function compileGraphQLTypesWatch(runner: LabeledProcessRunner) {
await runner.runCommandAndOutput('gql deps', ['yarn', 'install'], '')

runner.runCommandAndOutput(
'gqlgen',
['npx', 'lerna', 'run', 'gqlgen:watch'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { contractRevisionWithRatesSchema } from './revisionTypes'
// submissions are kept intact here across time
const contractSchema = z.object({
id: z.string().uuid(),
status: z.union([z.literal('SUBMITTED'), z.literal('DRAFT')]),
status: z.union([
z.literal('SUBMITTED'),
z.literal('DRAFT'),
z.literal('UNLOCKED'),
z.literal('RESUBMITTED'),
]),
stateCode: z.string(),
stateNumber: z.number().min(1),
// If this contract is in a DRAFT or UNLOCKED status, there will be a draftRevision
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
import type {
ActuaryCommunicationType,
ActuaryContact,
HealthPlanFormDataType,
RateInfoType,
SubmissionDocument,
} from 'app-web/src/common-code/healthPlanFormDataType'
import type {
HealthPlanPackageType,
HealthPlanRevisionType,
} from '../HealthPlanPackageType'
import type { ContractType } from './contractTypes'
import {
toDomain,
toProtoBuffer,
} from 'app-web/src/common-code/proto/healthPlanFormDataProto'
import type { ContractRevisionWithRatesType } from './revisionTypes'
import {
isSubmissionError,
parseAndSubmit,
} from '../../resolvers/healthPlanPackage/submitHealthPlanPackage'

function convertContractWithRatesToUnlockedHPP(
contract: ContractType
): HealthPlanPackageType | Error {
console.info('Attempting to convert contract to health plan package')

// Since drafts come in separate on the Contract type, we push it onto the revisions before converting below
if (contract.draftRevision) {
contract.revisions.unshift(contract.draftRevision)
}

const healthPlanRevisions = convertContractWithRatesRevtoHPPRev(contract)

if (healthPlanRevisions instanceof Error) {
return healthPlanRevisions
}

return {
id: contract.id,
stateCode: contract.stateCode,
revisions: healthPlanRevisions,
}
}

function convertContractWithRatesRevtoHPPRev(
contract: ContractType
): HealthPlanRevisionType[] | Error {
let healthPlanRevisions: HealthPlanRevisionType[] | Error = []
for (const contractRev of contract.revisions) {
const unlockedHealthPlanFormData = convertContractWithRatesToFormData(
contractRev,
contract.id,
contract.stateCode,
contract.stateNumber
)

if (unlockedHealthPlanFormData instanceof Error) {
return unlockedHealthPlanFormData
}

const formDataProto = toProtoBuffer(unlockedHealthPlanFormData)

// check that we can encode then decode with no issues
const domainData = toDomain(formDataProto)

// If any revision has en error in decoding we break the loop and return an error
if (domainData instanceof Error) {
healthPlanRevisions = new Error(
`Could not convert contract revision with ID: ${contractRev.id} to health plan package revision: ${domainData}`
)
break
}

const healthPlanRevision: HealthPlanRevisionType = {
id: contractRev.id,
unlockInfo: contractRev.unlockInfo,
submitInfo: contractRev.submitInfo,
createdAt: contractRev.createdAt,
formDataProto,
}

healthPlanRevisions.push(healthPlanRevision)
}

return healthPlanRevisions
}

// TODO: Clean up parameters into args and improve types to make things more strict
function convertContractWithRatesToFormData(
contractRev: ContractRevisionWithRatesType,
contractID: string,
stateCode: string,
stateNumber: number
): HealthPlanFormDataType | Error {
// additional certifying actuaries are on every rate post refactor but on the package pre-refactor
const pkgAdditionalCertifyingActuaries: Set<ActuaryContact> = new Set()
let pkgActuaryCommsPref: ActuaryCommunicationType | undefined = undefined

const rateInfos: RateInfoType[] = contractRev.rateRevisions.map(
(rateRev) => {
const {
rateType,
rateCapitationType,
rateCertificationName,
rateDateCertified,
rateDateEnd,
rateDateStart,
rateDocuments = [],
supportingDocuments = [],
rateProgramIDs,
packagesWithSharedRateCerts,
certifyingActuaryContacts = [],
addtlActuaryContacts = [],
amendmentEffectiveDateEnd,
amendmentEffectiveDateStart,
actuaryCommunicationPreference,
} = rateRev.formData

for (const additionalActuary of addtlActuaryContacts) {
pkgAdditionalCertifyingActuaries.add(additionalActuary)
}

// The first time we find a rate that has an actuary comms pref, we use that to set the package's prefs
if (actuaryCommunicationPreference && !pkgActuaryCommsPref) {
pkgActuaryCommsPref = actuaryCommunicationPreference
}

const rateAmendmentInfo = (amendmentEffectiveDateStart ||
amendmentEffectiveDateEnd) && {
effectiveDateStart: amendmentEffectiveDateStart,
effectiveDateEnd: amendmentEffectiveDateEnd,
}
return {
id: rateRev.id, // rate form data id is the revision ID
rateType,
rateCapitationType,
rateDocuments: rateDocuments.map((doc) => ({
...doc,
documentCategories: ['RATES'],
})) as SubmissionDocument[],
supportingDocuments: supportingDocuments.map((doc) => ({
...doc,
documentCategories: ['RATES_RELATED'],
})) as SubmissionDocument[],
rateAmendmentInfo: rateAmendmentInfo,
rateDateStart,
rateDateEnd,
rateDateCertified,
rateProgramIDs,
rateCertificationName,
actuaryContacts: certifyingActuaryContacts ?? [],
actuaryCommunicationPreference,
packagesWithSharedRateCerts,
}
}
)

// since this data is coming out from the DB without validation, we start by making a draft.
const healthPlanFormData: HealthPlanFormDataType = {
id: contractID, // contract form data id is the contract ID.
status: 'DRAFT',
createdAt: contractRev.createdAt,
updatedAt: contractRev.updatedAt,
stateCode: stateCode,
stateNumber: stateNumber,
programIDs: contractRev.formData.programIDs,
populationCovered: contractRev.formData.populationCovered,
submissionType: contractRev.formData.submissionType,
riskBasedContract: contractRev.formData.riskBasedContract,
submissionDescription: contractRev.formData.submissionDescription,
stateContacts: contractRev.formData.stateContacts,
addtlActuaryCommunicationPreference: pkgActuaryCommsPref,
addtlActuaryContacts: [...pkgAdditionalCertifyingActuaries],
documents: contractRev.formData.supportingDocuments.map((doc) => ({
...doc,
documentCategories: ['CONTRACT_RELATED'],
})) as SubmissionDocument[],
contractType: contractRev.formData.contractType,
contractExecutionStatus: contractRev.formData.contractExecutionStatus,
contractDocuments: contractRev.formData.contractDocuments.map(
(doc) => ({
...doc,
documentCategories: ['CONTRACT'],
})
) as SubmissionDocument[],
contractDateStart: contractRev.formData.contractDateStart,
contractDateEnd: contractRev.formData.contractDateEnd,
managedCareEntities: contractRev.formData.managedCareEntities,
federalAuthorities: contractRev.formData.federalAuthorities,
contractAmendmentInfo: {
modifiedProvisions: {
inLieuServicesAndSettings:
contractRev.formData.inLieuServicesAndSettings,
modifiedBenefitsProvided:
contractRev.formData.modifiedBenefitsProvided,
modifiedGeoAreaServed:
contractRev.formData.modifiedGeoAreaServed,
modifiedMedicaidBeneficiaries:
contractRev.formData.modifiedMedicaidBeneficiaries,
modifiedRiskSharingStrategy:
contractRev.formData.modifiedRiskSharingStrategy,
modifiedIncentiveArrangements:
contractRev.formData.modifiedIncentiveArrangements,
modifiedWitholdAgreements:
contractRev.formData.modifiedWitholdAgreements,
modifiedStateDirectedPayments:
contractRev.formData.modifiedStateDirectedPayments,
modifiedPassThroughPayments:
contractRev.formData.modifiedPassThroughPayments,
modifiedPaymentsForMentalDiseaseInstitutions:
contractRev.formData
.modifiedPaymentsForMentalDiseaseInstitutions,
modifiedMedicalLossRatioStandards:
contractRev.formData.modifiedMedicalLossRatioStandards,
modifiedOtherFinancialPaymentIncentive:
contractRev.formData.modifiedOtherFinancialPaymentIncentive,
modifiedEnrollmentProcess:
contractRev.formData.modifiedEnrollmentProcess,
modifiedGrevienceAndAppeal:
contractRev.formData.modifiedGrevienceAndAppeal,
modifiedNetworkAdequacyStandards:
contractRev.formData.modifiedNetworkAdequacyStandards,
modifiedLengthOfContract:
contractRev.formData.modifiedLengthOfContract,
modifiedNonRiskPaymentArrangements:
contractRev.formData.modifiedNonRiskPaymentArrangements,
},
},
rateInfos,
}

if (contractRev.submitInfo) {
const result = parseAndSubmit(healthPlanFormData)
if (isSubmissionError(result)) {
console.error(
'Failed to parse contract data into submitted HPFD',
result
)
return new Error(
'The data did not parse correctly as a submitted health plan'
)
}

return result
}

return healthPlanFormData
}

export {
convertContractWithRatesRevtoHPPRev,
convertContractWithRatesToUnlockedHPP,
convertContractWithRatesToFormData,
}
13 changes: 9 additions & 4 deletions services/app-api/src/domain-models/contractAndRates/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export { rateSchema, draftRateSchema } from './rateTypes'

export type { RateType } from './rateTypes'

export { contractSchema, draftContractSchema } from './contractTypes'

export { contractFormDataSchema, rateFormDataSchema } from './formDataTypes'
Expand All @@ -13,9 +11,16 @@ export {
rateRevisionSchema,
} from './revisionTypes'

export type { ContractType, DraftContractType } from './contractTypes'
export {
convertContractWithRatesRevtoHPPRev,
convertContractWithRatesToUnlockedHPP,
convertContractWithRatesToFormData,
} from './convertContractWithRatesToHPP'

export type { ContractType } from './contractTypes'
export type { RateType } from './rateTypes'

export type { ContractStatusType, UpdateInfoType } from './updateInfoType'
export type { PackageStatusType, UpdateInfoType } from './updateInfoType'

export type {
ContractFormDataType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import {

const rateSchema = z.object({
id: z.string().uuid(),
status: z.union([z.literal('SUBMITTED'), z.literal('DRAFT')]),
status: z.union([
z.literal('SUBMITTED'),
z.literal('DRAFT'),
z.literal('UNLOCKED'),
z.literal('RESUBMITTED'),
]),
stateCode: z.string(),
stateNumber: z.number().min(1),
// If this rate is in a DRAFT or UNLOCKED status, there will be a draftRevision
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const updateInfoSchema = z.object({
})

type UpdateInfoType = z.infer<typeof updateInfoSchema>
type ContractStatusType = z.infer<typeof contractSchema.shape.status>
type PackageStatusType = z.infer<typeof contractSchema.shape.status>

export type { ContractStatusType, UpdateInfoType }
export type { PackageStatusType , UpdateInfoType }

export { updateInfoSchema }
Loading
Loading