Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions backend/src/sync/eligibility/eligibility.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const PROTECTED_STATUSES = [
CSA_STATUS.BATCH_SENT_CANCELLATION,
CSA_STATUS.APPLICATION_REFUSED_CRA,
CSA_STATUS.CANCELLATION_REFUSED_CRA,
CSA_STATUS.CRA_ERROR_APPLICATION,
CSA_STATUS.CRA_ERROR_CANCELLATION,
CSA_STATUS.OVER_18,
] as const

Expand Down
6 changes: 6 additions & 0 deletions backend/src/sync/eligibility/eligibility.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing'
import { PrismaService } from 'src/common/database/prisma.service'
import { AppLogger } from 'src/common/logger/app-logger'
import { CSA_STATUS } from 'src/common/state-machine/constants/csa-status.constants'
import { pacificToday } from 'src/common/utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { PROTECTED_STATUSES, PROTECTED_STATUSES_SQL } from './eligibility.config'
Expand All @@ -22,6 +23,11 @@ describe('PROTECTED_STATUSES_SQL', () => {
const count = PROTECTED_STATUSES_SQL.split(',').length
expect(count).toBe(PROTECTED_STATUSES.length)
})

it('should include CRA error statuses as protected', () => {
expect(PROTECTED_STATUSES).toContain(CSA_STATUS.CRA_ERROR_APPLICATION)
expect(PROTECTED_STATUSES).toContain(CSA_STATUS.CRA_ERROR_CANCELLATION)
})
})

describe('EligibilityService', () => {
Expand Down
112 changes: 104 additions & 8 deletions backend/src/sync/eligibility/eligibility.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,27 @@ export function selectPrimaryRecords(
}

const primaryPlacement = selectPrimaryPlacement(profile.placements)
const placementContractKey = normalize(primaryPlacement?.contractNumber)
const placementProviderKey = normalize(primaryPlacement?.providerId)

// Primary Order: match via primary placement's link key
let primaryOrder: OrderRecord | null = null
if (primaryPlacement?.source === 'ICM' && primaryPlacement.agreementRowId) {
primaryOrder =
profile.orders.find((order) => order.agreementRowId === primaryPlacement.agreementRowId) ??
null
if (!primaryOrder && placementContractKey) {
primaryOrder =
profile.orders.find(
(order) =>
order.source === 'MIS' && normalize(order.contractNumber) === placementContractKey,
) ?? null
}
} else if (primaryPlacement?.source === 'MIS' && primaryPlacement.contractNumber) {
primaryOrder =
profile.orders.find(
(order) =>
order.source === 'MIS' && order.contractNumber === primaryPlacement.contractNumber,
order.source === 'MIS' && normalize(order.contractNumber) === placementContractKey,
) ?? null
}

Expand All @@ -106,12 +115,22 @@ export function selectPrimaryRecords(
primaryAgreement =
profile.agreements.find((agreement) => agreement.rowId === primaryPlacement.agreementRowId) ??
null
if (!primaryAgreement && placementContractKey) {
primaryAgreement =
profile.agreements.find(
(agreement) =>
agreement.source === 'MIS' &&
normalize(agreement.contractNumber) === placementContractKey &&
normalize(agreement.providerId) === placementProviderKey,
) ?? null
}
} else if (primaryPlacement?.source === 'MIS' && primaryPlacement.contractNumber) {
primaryAgreement =
profile.agreements.find(
(agreement) =>
agreement.source === 'MIS' &&
agreement.contractNumber === primaryPlacement.contractNumber,
normalize(agreement.contractNumber) === placementContractKey &&
normalize(agreement.providerId) === placementProviderKey,
) ?? null
}

Expand All @@ -132,16 +151,30 @@ function selectOocPrimaryRecords(
primaryOrder: OrderRecord | null
primaryAgreement: AgreementRecord | null
} {
const primaryAgreement = selectOocPrimaryAgreement(profile.agreements)
const primaryOrder =
primaryAgreement?.rowId != null
? findClosedIcmOrderPreviousMonth(profile.orders, primaryAgreement.rowId, referenceDate)
: null
const primaryAgreement = selectOocPrimaryAgreement(profile.agreements, profile.placements)
let primaryOrder: OrderRecord | null = null

if (primaryAgreement?.source === 'ICM' && primaryAgreement.rowId != null) {
primaryOrder = findClosedIcmOrderPreviousMonth(
profile.orders,
primaryAgreement.rowId,
referenceDate,
)
} else if (primaryAgreement?.source === 'MIS' && primaryAgreement.contractNumber != null) {
primaryOrder = findClosedMisPaymentPreviousMonth(
profile.orders,
primaryAgreement.contractNumber,
referenceDate,
)
}

return { primaryPlacement: null, primaryOrder, primaryAgreement }
}

function selectOocPrimaryAgreement(agreements: AgreementRecord[]): AgreementRecord | null {
function selectOocPrimaryAgreement(
agreements: AgreementRecord[],
placements: PlacementRecord[],
): AgreementRecord | null {
const oocAgreements = agreements.filter(
(agreement) =>
agreement.source === 'ICM' && normalize(agreement.agreementType) === 'OUT OF CARE',
Expand All @@ -154,6 +187,46 @@ function selectOocPrimaryAgreement(agreements: AgreementRecord[]): AgreementReco
if (active) return active

const withEndDate = oocAgreements.filter((agreement) => agreement.agreementEndDate != null)
if (withEndDate.length > 0) {
return withEndDate.reduce((latest, current) =>
current.agreementEndDate!.getTime() > latest.agreementEndDate!.getTime() ? current : latest,
)
}

return selectOocMisAgreement(agreements, placements)
}

function selectOocMisAgreement(
agreements: AgreementRecord[],
placements: PlacementRecord[],
): AgreementRecord | null {
const misPlacementKeys = new Set(
placements
.filter((placement) => placement.source === 'MIS')
.map((placement) => {
const contractNumber = normalize(placement.contractNumber)
const providerId = normalize(placement.providerId)
if (!contractNumber || !providerId) return null
return `${contractNumber}::${providerId}`
})
.filter((key): key is string => key != null),
)
if (misPlacementKeys.size === 0) return null

const misContracts = agreements.filter(
(agreement) =>
agreement.source === 'MIS' &&
agreement.contractNumber != null &&
misPlacementKeys.has(
`${normalize(agreement.contractNumber)}::${normalize(agreement.providerId)}`,
),
)
if (misContracts.length === 0) return null

const active = misContracts.find((agreement) => normalize(agreement.agreementStatus) === 'ACTIVE')
if (active) return active

const withEndDate = misContracts.filter((agreement) => agreement.agreementEndDate != null)
if (withEndDate.length === 0) return null

return withEndDate.reduce((latest, current) =>
Expand Down Expand Up @@ -181,6 +254,29 @@ function findClosedIcmOrderPreviousMonth(
)
}

function findClosedMisPaymentPreviousMonth(
orders: OrderRecord[],
contractNumber: string,
referenceDate: Date,
): OrderRecord | null {
const prevMonth = getPreviousMonth(referenceDate)
const normalizedContractNumber = normalize(contractNumber)
if (!normalizedContractNumber) return null

const matching = orders.filter(
(order) =>
order.source === 'MIS' &&
normalize(order.contractNumber) === normalizedContractNumber &&
normalize(order.orderStatus) === 'CLOSED' &&
isInMonth(order.effectiveStartDate, prevMonth),
)
if (matching.length === 0) return null

return matching.reduce((highest, current) =>
current.amount > highest.amount ? current : highest,
)
}

const SOURCE_TYPE_PRIORITY: Array<{ source: 'ICM' | 'MIS'; type: string }> = [
{ source: 'ICM', type: 'PLACEMENT' },
{ source: 'ICM', type: 'NON-PLACEMENT LOCATION' },
Expand Down
Loading
Loading