Skip to content

Commit

Permalink
Merge pull request #720 from ministryofjustice/BICAWS7-3068-extract-o…
Browse files Browse the repository at this point in the history
…ut-functions

Refactor out functions for the OffenceMatching component
  • Loading branch information
richrace authored Sep 6, 2024
2 parents 7011401 + 2385f82 commit 8391826
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 146 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ commands:
- restore_cache:
keys:
- ubuntu-package-cache-v1-{{ checksum "~/bichard7-next-core/.circleci/scripts/install-ms-edge-on-linux.sh" }}
- ubuntu-package-cache-v1-
- run:
name: Install MS Edge (dev)
working_directory: ~/bichard7-next-core
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ describe("View offence matching exceptions", () => {
{ asn: ExceptionCode.HO100304, firstOffenceBadge: "Unmatched" },
{ asn: ExceptionCode.HO100328, firstOffenceBadge: "Unmatched" },
{ asn: ExceptionCode.HO100507, firstOffenceBadge: "Added by Court", secondOffenceBadge: "Added by Court" },
{ offenceReasonSequence: ExceptionCode.HO100203 },
{ offenceReasonSequence: ExceptionCode.HO100228 },
{ offenceReasonSequence: ExceptionCode.HO100311 },
{ offenceReasonSequence: ExceptionCode.HO100312 },
{ offenceReasonSequence: ExceptionCode.HO100320 },
{ offenceReasonSequence: ExceptionCode.HO100329 },
{ offenceReasonSequence: ExceptionCode.HO100333 }
{ offenceReasonSequence: ExceptionCode.HO100203, firstOffenceBadge: "Unmatched" },
{ offenceReasonSequence: ExceptionCode.HO100228, firstOffenceBadge: "Unmatched" },
{ offenceReasonSequence: ExceptionCode.HO100311, firstOffenceBadge: "Unmatched" },
{ offenceReasonSequence: ExceptionCode.HO100312, firstOffenceBadge: "Unmatched" },
{ offenceReasonSequence: ExceptionCode.HO100320, firstOffenceBadge: "Unmatched" },
{ offenceReasonSequence: ExceptionCode.HO100329, firstOffenceBadge: "Unmatched" },
{ offenceReasonSequence: ExceptionCode.HO100333, firstOffenceBadge: "Unmatched" }
].forEach(({ asn, offenceReasonSequence, firstOffenceBadge = "Matched", secondOffenceBadge = "Unmatched" }) => {
it(`Should display the correct error for ${asn ?? offenceReasonSequence}`, () => {
cy.task("clearCourtCases")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,20 @@
import { Offence } from "@moj-bichard7-developers/bichard7-next-core/core/types/AnnotatedHearingOutcome"
import ExceptionCode from "@moj-bichard7-developers/bichard7-next-data/dist/types/ExceptionCode"
import Badge, { BadgeColours } from "components/Badge"
import ConditionalRender from "components/ConditionalRender"
import ErrorPromptMessage from "components/ErrorPromptMessage"
import ExceptionFieldTableRow from "components/ExceptionFieldTableRow"
import { useCourtCase } from "context/CourtCaseContext"
import { useCurrentUser } from "context/CurrentUserContext"
import { findExceptions } from "types/ErrorMessages"
import { DisplayFullUser } from "types/display/Users"
import { Exception } from "types/exceptions"
import getExceptionDefinition from "utils/getExceptionDefinition"
import getExceptionMessage from "utils/offenceMatcher/getExceptionMessage"
import getOffenceMatchingException from "utils/offenceMatcher/getOffenceMatchingException"
import isEnabled from "utils/offenceMatcher/isEnabled"
import offenceMatchingExceptions from "utils/offenceMatcher/offenceMatchingExceptions"
import findCandidates from "../../../../../../utils/offenceMatcher/findCandidates"
import { TableRow } from "../../TableRow"
import { getOffenceMatchingException } from "utils/offenceMatcher/getOffenceMatchingException"
import OffenceMatcher from "./OffenceMatcher"

const enabled = (user: DisplayFullUser) => {
const enabledInProduction = true // change this if we need to disable in production for everyone
const { exceptionsEnabled, offenceMatchingEnabled } = user.featureFlags
const featureFlagsEnabled = exceptionsEnabled && offenceMatchingEnabled

const isProduction = process.env.WORKSPACE === "production"
if (!isProduction) {
return featureFlagsEnabled
}
return enabledInProduction && featureFlagsEnabled
}

type Props = {
type OffenceMatchingProps = {
offenceIndex: number
offence: Offence
isCaseUnresolved: boolean
Expand All @@ -41,37 +28,15 @@ export const OffenceMatching = ({
isCaseUnresolved,
exceptions,
isCaseLockedToCurrentUser
}: Props) => {
}: OffenceMatchingProps) => {
const { courtCase, savedAmendments } = useCourtCase()
const currentUser = useCurrentUser()

const offenceMatchingExceptions = [ExceptionCode.HO100310, ExceptionCode.HO100332]
const noneOffenceMatchingExceptions = [
ExceptionCode.HO100203,
ExceptionCode.HO100228,
ExceptionCode.HO100304,
ExceptionCode.HO100311,
ExceptionCode.HO100312,
ExceptionCode.HO100320,
ExceptionCode.HO100328,
ExceptionCode.HO100333,
ExceptionCode.HO100507
]

const offenceMatchingException = isCaseUnresolved && getOffenceMatchingException(exceptions, offenceIndex)

const findExceptionByOffenceNumber = courtCase.aho.Exceptions.filter((exception) =>
exception.path.includes(offenceIndex)
)
const offenceMatchingExceptionMessage =
findExceptions(
courtCase,
findExceptionByOffenceNumber.length > 0 ? findExceptionByOffenceNumber : courtCase.aho.Exceptions,
...noneOffenceMatchingExceptions
) || getExceptionDefinition(findExceptionByOffenceNumber[0]?.code)?.shortDescription
const offenceMatchingExceptionMessage = getExceptionMessage(courtCase, offenceIndex)

const displayOffenceMatcher =
enabled(currentUser) && exceptions.some((e) => offenceMatchingExceptions.includes(e.code))
isEnabled(currentUser) && exceptions.some((e) => offenceMatchingExceptions.offenceNotMatched.includes(e.code))
const userCanMatchOffence =
courtCase.errorLockedByUsername === currentUser?.username && courtCase.errorStatus === "Unresolved"

Expand Down
4 changes: 2 additions & 2 deletions src/utils/getTabDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { Amendments } from "types/Amendments"
import { Exception } from "types/exceptions"
import getNextHearingDateExceptions from "./exceptions/getNextHearingDateExceptions"
import getNextHearingLocationExceptions from "./exceptions/getNextHearingLocationExceptions"
import { getOffenceMatchingExceptions } from "./offenceMatcher/getOffenceMatchingException"
import hasNextHearingDateExceptions from "./exceptions/hasNextHearingDateExceptions"
import hasNextHearingLocationException from "./exceptions/hasNextHearingLocationException"
import hasOffenceMatchingExceptions from "./offenceMatcher/hasOffenceMatchingExceptions"
import isAsnFormatValid from "./exceptions/isAsnFormatValid"
import isAsnException from "./exceptions/isException/isAsnException"
import getOffenceMatchingExceptions from "./offenceMatcher/getOffenceMatchingExceptions"
import hasOffenceMatchingExceptions from "./offenceMatcher/hasOffenceMatchingExceptions"

export type TabDetails = {
name: "Defendant" | "Hearing" | "Case" | "Offences" | "Notes"
Expand Down
8 changes: 4 additions & 4 deletions src/utils/offenceMatcher/findCandidates.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { CaseType } from "@moj-bichard7-developers/bichard7-next-core/core/phase1/enrichAho/enrichFunctions/matchOffencesToPnc/annotatePncMatch"
import generateCandidate from "@moj-bichard7-developers/bichard7-next-core/core/phase1/enrichAho/enrichFunctions/matchOffencesToPnc/generateCandidate"
import { DisplayFullCourtCase } from "../../types/display/CourtCases"
import { Candidates } from "../../types/OffenceMatching"
import generateCandidate from "@moj-bichard7-developers/bichard7-next-core/core/phase1/enrichAho/enrichFunctions/matchOffencesToPnc/generateCandidate"
import { CaseType } from "@moj-bichard7-developers/bichard7-next-core/core/phase1/enrichAho/enrichFunctions/matchOffencesToPnc/annotatePncMatch"

const findCandidates = (courtCase: DisplayFullCourtCase, offenceIndex: number): Candidates[] => {
if (!courtCase.aho.PncQuery || !courtCase.aho.PncQuery.courtCases) {
return []
}

const offence = courtCase.aho.AnnotatedHearingOutcome.HearingOutcome.Case.HearingDefendant.Offence[offenceIndex]
const candiates = courtCase.aho.PncQuery.courtCases
const candidates = courtCase.aho.PncQuery.courtCases
.map((c) => {
const matchForThisCase = c.offences.filter(
(pncOffence) =>
Expand All @@ -28,7 +28,7 @@ const findCandidates = (courtCase: DisplayFullCourtCase, offenceIndex: number):
})
.filter((e) => !!e)

return candiates as Candidates[]
return candidates as Candidates[]
}

export default findCandidates
83 changes: 83 additions & 0 deletions src/utils/offenceMatcher/getExceptionMessage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { AnnotatedHearingOutcome } from "@moj-bichard7-developers/bichard7-next-core/core/types/AnnotatedHearingOutcome"
import type { DisplayFullCourtCase } from "types/display/CourtCases"
import getExceptionMessage from "./getExceptionMessage"

describe("getExceptionMessage", () => {
let courtCase: DisplayFullCourtCase

beforeEach(() => {
courtCase = {
asn: "",
courtName: "",
errorId: 0,
errorStatus: "Unresolved",
errorReport: "",
isUrgent: false,
ptiurn: "",
triggerCount: 0,
defendantName: "",
orgForPoliceFilter: null,
courtCode: null,
courtReference: "",
notes: [],
canUserEditExceptions: false,
triggers: [],
resolutionTimestamp: null,
aho: {} as AnnotatedHearingOutcome,
updatedHearingOutcome: {} as AnnotatedHearingOutcome
}
})

it("returns undefined if there's no exceptions in the aho", () => {
const result = getExceptionMessage(courtCase, 0)
expect(result).toBeUndefined()
})

it("returns the exception message if there is exceptions in the aho and it's not present ErrorMessages file", () => {
courtCase.aho = {
Exceptions: [{ code: "HO100310", path: ["path", "to", 0, "offence"] }]
} as AnnotatedHearingOutcome

const result = getExceptionMessage(courtCase, 0)
expect(result).toBe("Multiple court Offences with different Results match a PNC offence")
})

it("returns the exception message if there is exceptions in the aho and it's present ErrorMessages file", () => {
courtCase.aho = {
Exceptions: [{ code: "HO100507", path: ["path", "to", 0, "offence"] }]
} as AnnotatedHearingOutcome

const result = getExceptionMessage(courtCase, 0)
expect(result).toBe(
"Offences have been added in court to a Penalty case. This needs to be manually resolved on the PNC to deal with error, and then manually resolved in Bichard."
)
})

it("returns undefined if the exception is resolved", () => {
courtCase.errorStatus = "Resolved"
courtCase.aho = {
Exceptions: [{ code: "HO100310", path: ["path", "to", 0, "offence"] }]
} as AnnotatedHearingOutcome

const result = getExceptionMessage(courtCase, 0)
expect(result).toBeUndefined()
})

it("returns undefined if the exception is not found", () => {
courtCase.aho = {
Exceptions: [{ code: "HO100110", path: ["path", "to", 0, "offence"] }]
} as AnnotatedHearingOutcome

const result = getExceptionMessage(courtCase, 0)
expect(result).toBeUndefined()
})

it("returns undefined if the exception is not found with the offenceIndex begin changed", () => {
courtCase.aho = {
Exceptions: [{ code: "HO100110", path: ["path", "to", 0, "offence"] }]
} as AnnotatedHearingOutcome

const result = getExceptionMessage(courtCase, 1)
expect(result).toBeUndefined()
})
})
35 changes: 35 additions & 0 deletions src/utils/offenceMatcher/getExceptionMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type ExceptionCode from "@moj-bichard7-developers/bichard7-next-data/dist/types/ExceptionCode"
import type { DisplayFullCourtCase } from "types/display/CourtCases"
import { findExceptions } from "types/ErrorMessages"
import getExceptionDefinition from "utils/getExceptionDefinition"
import offenceMatchingExceptions from "./offenceMatchingExceptions"

type ExceptionCodeAndPath = {
code: ExceptionCode
path: (string | number)[]
}

const findExceptionByOffenceNumber = (courtCase: DisplayFullCourtCase, offenceIndex: number): ExceptionCodeAndPath[] =>
courtCase.aho.Exceptions?.filter((exception) => exception.path.includes(offenceIndex))

const getExceptionMessage = (courtCase: DisplayFullCourtCase, offenceIndex: number): string | undefined => {
const exceptionByOffenceNumber: ExceptionCodeAndPath[] = findExceptionByOffenceNumber(courtCase, offenceIndex)

if (exceptionByOffenceNumber === undefined) {
return undefined
}

if (courtCase.errorStatus === "Resolved") {
return undefined
}

return (
findExceptions(
courtCase,
exceptionByOffenceNumber.length > 0 ? exceptionByOffenceNumber : courtCase.aho.Exceptions,
...offenceMatchingExceptions.noOffencesMatched
) || getExceptionDefinition(exceptionByOffenceNumber[0]?.code)?.shortDescription
)
}

export default getExceptionMessage
37 changes: 3 additions & 34 deletions src/utils/offenceMatcher/getOffenceMatchingException.test.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
import { Exception } from "types/exceptions"
import {
getOffenceMatchingException,
GetOffenceMatchingExceptionResult,
getOffenceMatchingExceptions
} from "./getOffenceMatchingException"
import ExceptionCode from "@moj-bichard7-developers/bichard7-next-data/dist/types/ExceptionCode"
import type { Exception } from "types/exceptions"
import { ExceptionBadgeType } from "utils/exceptions/exceptionBadgeType"

describe("getOffenceMatchingExceptions", () => {
it("returns an empty array when given no exceptions", () => {
const result = getOffenceMatchingExceptions([])
expect(result).toEqual([])
})

it("returns an empty array when given exception that is not in array of valid values", () => {
const exception: Exception = { code: ExceptionCode.HO100304, path: ["test"] }
const result = getOffenceMatchingExceptions([exception])
expect(result).toEqual([])
})

it("returns exception when given exception that is in array of valid values", () => {
const exception: Exception = { code: ExceptionCode.HO100310, path: ["test"] }
const result = getOffenceMatchingExceptions([exception])
expect(result).toEqual([exception])
})

it("returns exception when given multiple exceptions and exception is in array of valid values", () => {
const exceptions: Exception[] = [
{ code: ExceptionCode.HO100304, path: ["test"] },
{ code: ExceptionCode.HO100310, path: ["test"] }
]
const result = getOffenceMatchingExceptions(exceptions)
expect(result).toEqual([exceptions[1]])
})
})
import type { GetOffenceMatchingExceptionResult } from "./getOffenceMatchingException"
import getOffenceMatchingException from "./getOffenceMatchingException"

describe("getOffenceMatchingException", () => {
it("returns undefined when given an empty array", () => {
Expand Down
21 changes: 2 additions & 19 deletions src/utils/offenceMatcher/getOffenceMatchingException.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,9 @@ import ExceptionCode from "@moj-bichard7-developers/bichard7-next-data/dist/type
import { isEqual } from "lodash"
import { Exception } from "types/exceptions"
import { ExceptionBadgeType } from "../exceptions/exceptionBadgeType"
import offenceMatchingExceptions from "./offenceMatchingExceptions"

const getOffenceReasonSequencePath = (offenceIndex: number) => errorPaths.offence(offenceIndex).reasonSequence
const offenceMatchingExceptions = {
noOffencesMatched: [ExceptionCode.HO100304, ExceptionCode.HO100328, ExceptionCode.HO100507],
offenceNotMatched: [
ExceptionCode.HO100203,
ExceptionCode.HO100228,
ExceptionCode.HO100310,
ExceptionCode.HO100311,
ExceptionCode.HO100312,
ExceptionCode.HO100320,
ExceptionCode.HO100329,
ExceptionCode.HO100332,
ExceptionCode.HO100333
]
}

type GetOffenceMatchingExceptionResult =
| {
Expand All @@ -27,10 +14,6 @@ type GetOffenceMatchingExceptionResult =
}
| undefined

const getOffenceMatchingExceptions = (exceptions: Exception[]): Exception[] => {
return exceptions.filter((exception) => offenceMatchingExceptions.offenceNotMatched.includes(exception.code))
}

const getOffenceMatchingException = (
exceptions: Exception[],
offenceIndex: number
Expand Down Expand Up @@ -61,5 +44,5 @@ const getOffenceMatchingException = (
}
}

export { getOffenceMatchingException, getOffenceMatchingExceptions }
export default getOffenceMatchingException
export type { GetOffenceMatchingExceptionResult }
31 changes: 31 additions & 0 deletions src/utils/offenceMatcher/getOffenceMatchingExceptions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import ExceptionCode from "@moj-bichard7-developers/bichard7-next-data/dist/types/ExceptionCode"
import type { Exception } from "types/exceptions"
import getOffenceMatchingExceptions from "./getOffenceMatchingExceptions"

describe("getOffenceMatchingExceptions", () => {
it("returns an empty array when given no exceptions", () => {
const result = getOffenceMatchingExceptions([])
expect(result).toEqual([])
})

it("returns an empty array when given exception that is not in array of valid values", () => {
const exception: Exception = { code: ExceptionCode.HO100312, path: ["test"] }
const result = getOffenceMatchingExceptions([exception])
expect(result).toEqual([])
})

it("returns exception when given exception that is in array of valid values", () => {
const exception: Exception = { code: ExceptionCode.HO100310, path: ["test"] }
const result = getOffenceMatchingExceptions([exception])
expect(result).toEqual([exception])
})

it("returns exception when given multiple exceptions and exception is in array of valid values", () => {
const exceptions: Exception[] = [
{ code: ExceptionCode.HO100310, path: ["test"] },
{ code: ExceptionCode.HO100312, path: ["test"] }
]
const result = getOffenceMatchingExceptions(exceptions)
expect(result).toEqual([exceptions[0]])
})
})
7 changes: 7 additions & 0 deletions src/utils/offenceMatcher/getOffenceMatchingExceptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Exception } from "types/exceptions"
import offenceMatchingExceptions from "./offenceMatchingExceptions"

const getOffenceMatchingExceptions = (exceptions: Exception[]): Exception[] =>
exceptions.filter((exception) => offenceMatchingExceptions.offenceNotMatched.includes(exception.code))

export default getOffenceMatchingExceptions
Loading

0 comments on commit 8391826

Please sign in to comment.