placementt-core 1.400.1187 → 1.400.1189

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,12 +37,12 @@ export declare const destinationsConfigCallables: {
37
37
  academicYear: z.ZodString;
38
38
  }, "strip", z.ZodTypeAny, {
39
39
  created: number;
40
- academicYear: string;
41
40
  provisioned: boolean;
41
+ academicYear: string;
42
42
  }, {
43
43
  created: number;
44
- academicYear: string;
45
44
  provisioned: boolean;
45
+ academicYear: string;
46
46
  }>;
47
47
  };
48
48
  };
@@ -2645,12 +2645,12 @@ export declare const allCallables: {
2645
2645
  academicYear: z.ZodString;
2646
2646
  }, "strip", z.ZodTypeAny, {
2647
2647
  created: number;
2648
- academicYear: string;
2649
2648
  provisioned: boolean;
2649
+ academicYear: string;
2650
2650
  }, {
2651
2651
  created: number;
2652
- academicYear: string;
2653
2652
  provisioned: boolean;
2653
+ academicYear: string;
2654
2654
  }>;
2655
2655
  };
2656
2656
  };
@@ -2690,12 +2690,12 @@ export declare const callableGroups: {
2690
2690
  academicYear: z.ZodString;
2691
2691
  }, "strip", z.ZodTypeAny, {
2692
2692
  created: number;
2693
- academicYear: string;
2694
2693
  provisioned: boolean;
2694
+ academicYear: string;
2695
2695
  }, {
2696
2696
  created: number;
2697
- academicYear: string;
2698
2697
  provisioned: boolean;
2698
+ academicYear: string;
2699
2699
  }>;
2700
2700
  };
2701
2701
  };
package/lib/kcsie.d.ts CHANGED
@@ -1,16 +1,22 @@
1
- import { StudentPlacementData, WorkflowStage } from "./typeDefinitions";
1
+ import { PlacementQuestions, StudentPlacementData, WorkflowStage } from "./typeDefinitions";
2
2
  type KcsiePerson = NonNullable<StudentPlacementData["kcsiePeople"]>[string];
3
3
  export declare const KCSIE_BARRED_LIST_URL = "https://check-the-childrens-barred-list.education.gov.uk/";
4
4
  export declare const KCSIE_STATUS_LABELS: Record<NonNullable<StudentPlacementData["kcsie"]>, string>;
5
5
  export declare const KCSIE_NOT_REQUESTED: Pick<StudentPlacementData, "kcsie" | "kcsieStatuses">;
6
+ export declare const KCSIE_PARENT_EXEMPT: Pick<StudentPlacementData, "kcsie" | "kcsieStatuses">;
6
7
  export declare const kcsiePersonId: ({ surname, dob }: Pick<KcsiePerson, "surname" | "dob">) => string;
7
- export declare const currentKcsieStatus: (person: Pick<KcsiePerson, "status" | "academicYear">, date?: Date) => KcsiePerson["status"];
8
+ export declare const kcsieCheckedOverAYearAgo: (person: Pick<KcsiePerson, "status" | "reviewed">, now?: Date) => boolean;
9
+ export declare const kcsiePeopleDetails: (people?: StudentPlacementData["kcsiePeople"]) => NonNullable<PlacementQuestions["kcsie"]>["people"];
10
+ export declare const matchingKcsiePeople: (previous: NonNullable<PlacementQuestions["kcsie"]>["people"], surname: string) => NonNullable<PlacementQuestions["kcsie"]>["people"];
8
11
  export declare const kcsieOutstanding: (status?: StudentPlacementData["kcsie"]) => boolean;
9
- export declare const workflowRequiresKcsie: (workflow?: WorkflowStage[]) => boolean;
12
+ export declare const stageRequiresKcsie: (stage: Pick<WorkflowStage, "kcsie" | "kcsieIncludeParentEmployers">, placement?: Pick<StudentPlacementData, "parentIsEmployer">) => boolean;
13
+ export declare const workflowRequiresKcsie: (workflow?: WorkflowStage[], placement?: Pick<StudentPlacementData, "parentIsEmployer">) => boolean;
14
+ export declare const kcsieExemptionUpdate: (workflow: WorkflowStage[] | undefined, placement: Pick<StudentPlacementData, "kcsie" | "parentIsEmployer">) => Pick<StudentPlacementData, "kcsie" | "kcsieStatuses"> | undefined;
10
15
  export declare const placementKcsieFields: (people: NonNullable<StudentPlacementData["kcsiePeople"]>) => Pick<StudentPlacementData, "kcsie" | "kcsieStatuses">;
11
16
  export declare const kcsieRowsForPlacement: (placementId: string, placement: StudentPlacementData, status?: StudentPlacementData["kcsie"]) => ({
12
17
  placementId: string;
13
18
  providerContactId: string | undefined;
19
+ parentIsEmployer: boolean | undefined;
14
20
  studentForename: string | undefined;
15
21
  studentSurname: string | undefined;
16
22
  employerName: string;
package/lib/kcsie.js CHANGED
@@ -3,9 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getKcsieCohortCounts = exports.reviewKcsiePerson = exports.kcsieRowsForPlacement = exports.placementKcsieFields = exports.workflowRequiresKcsie = exports.kcsieOutstanding = exports.currentKcsieStatus = exports.kcsiePersonId = exports.KCSIE_NOT_REQUESTED = exports.KCSIE_STATUS_LABELS = exports.KCSIE_BARRED_LIST_URL = void 0;
6
+ exports.getKcsieCohortCounts = exports.reviewKcsiePerson = exports.kcsieRowsForPlacement = exports.placementKcsieFields = exports.kcsieExemptionUpdate = exports.workflowRequiresKcsie = exports.stageRequiresKcsie = exports.kcsieOutstanding = exports.matchingKcsiePeople = exports.kcsiePeopleDetails = exports.kcsieCheckedOverAYearAgo = exports.kcsiePersonId = exports.KCSIE_PARENT_EXEMPT = exports.KCSIE_NOT_REQUESTED = exports.KCSIE_STATUS_LABELS = exports.KCSIE_BARRED_LIST_URL = void 0;
7
7
  const firestore_1 = require("firebase/firestore");
8
- const benchmarkEvidenceRules_1 = require("./benchmarkEvidenceRules");
9
8
  const firebaseQuery_1 = __importDefault(require("./firebase/firebaseQuery"));
10
9
  const firebaseQuery = new firebaseQuery_1.default();
11
10
  exports.KCSIE_BARRED_LIST_URL = "https://check-the-childrens-barred-list.education.gov.uk/";
@@ -16,18 +15,46 @@ exports.KCSIE_STATUS_LABELS = {
16
15
  rejected: "Rejected",
17
16
  notRequired: "Not required",
18
17
  notRequested: "Not yet requested",
18
+ parentExempt: "Exempt: employer is parent",
19
19
  };
20
20
  exports.KCSIE_NOT_REQUESTED = { kcsie: "notRequested", kcsieStatuses: ["notRequested"] };
21
+ exports.KCSIE_PARENT_EXEMPT = { kcsie: "parentExempt", kcsieStatuses: ["parentExempt"] };
21
22
  // Surname and DOB are what the DfE search uses, so they identify a person across placements.
22
23
  const kcsiePersonId = ({ surname, dob }) => `${surname.trim().toLowerCase().replace(/[^a-z]/g, "")}_${dob}`;
23
24
  exports.kcsiePersonId = kcsiePersonId;
24
- // Outcomes from an earlier academic year need checking again.
25
- const currentKcsieStatus = (person, date = new Date()) => person.academicYear === (0, benchmarkEvidenceRules_1.academicYearForDate)(date) ? person.status : "pending";
26
- exports.currentKcsieStatus = currentKcsieStatus;
25
+ // Outcomes carry over indefinitely; the school is just told when one is over a year old.
26
+ const kcsieCheckedOverAYearAgo = (person, now = new Date()) => person.status !== "pending" && Boolean(person.reviewed) && now.getTime() - new Date(person.reviewed).getTime() > 365 * 24 * 60 * 60 * 1000;
27
+ exports.kcsieCheckedOverAYearAgo = kcsieCheckedOverAYearAgo;
28
+ // Just the details the employer enters, e.g. to import people from another placement.
29
+ const kcsiePeopleDetails = (people) => Object.values(people || {}).map(({ forename, surname, dob, previousSurnames }) => ({ forename, surname, dob, ...(previousSurnames?.length ? { previousSurnames } : {}) }));
30
+ exports.kcsiePeopleDetails = kcsiePeopleDetails;
31
+ // Previous individuals whose surname starts with what's been typed, for the employer to pick from.
32
+ const matchingKcsiePeople = (previous, surname) => {
33
+ const typed = surname.trim().toLowerCase();
34
+ return typed.length < 2 ? [] : previous.filter((p) => p.surname.toLowerCase().startsWith(typed));
35
+ };
36
+ exports.matchingKcsiePeople = matchingKcsiePeople;
27
37
  const kcsieOutstanding = (status) => status === "pending" || status === "flagged" || status === "rejected";
28
38
  exports.kcsieOutstanding = kcsieOutstanding;
29
- const workflowRequiresKcsie = (workflow) => Boolean(workflow?.some((s) => s.kcsie && s.userType === "Provider"));
39
+ // Parent employers are exempt unless the stage opts them in.
40
+ const stageRequiresKcsie = (stage, placement) => Boolean(stage.kcsie) && !(placement?.parentIsEmployer && !stage.kcsieIncludeParentEmployers);
41
+ exports.stageRequiresKcsie = stageRequiresKcsie;
42
+ // Without a placement, whether any placement on the workflow could need a check.
43
+ const workflowRequiresKcsie = (workflow, placement) => Boolean(workflow?.some((s) => s.userType === "Provider" && (0, exports.stageRequiresKcsie)(s, placement)));
30
44
  exports.workflowRequiresKcsie = workflowRequiresKcsie;
45
+ // The KCSIE fields to set once it's known whether the employer is an exempt parent, or undefined to leave them.
46
+ // Placements the employer has already answered for keep their answer.
47
+ const kcsieExemptionUpdate = (workflow, placement) => {
48
+ if (!(0, exports.workflowRequiresKcsie)(workflow))
49
+ return undefined;
50
+ const exempt = !(0, exports.workflowRequiresKcsie)(workflow, placement);
51
+ if (exempt && (!placement.kcsie || placement.kcsie === "notRequested"))
52
+ return exports.KCSIE_PARENT_EXEMPT;
53
+ if (!exempt && (!placement.kcsie || placement.kcsie === "parentExempt"))
54
+ return exports.KCSIE_NOT_REQUESTED;
55
+ return undefined;
56
+ };
57
+ exports.kcsieExemptionUpdate = kcsieExemptionUpdate;
31
58
  // The worst person's status wins, so one rejection holds the placement.
32
59
  const placementKcsieFields = (people) => {
33
60
  const statuses = Object.values(people).map((p) => p.status);
@@ -37,7 +64,7 @@ const placementKcsieFields = (people) => {
37
64
  exports.placementKcsieFields = placementKcsieFields;
38
65
  // One row per person, so a placement naming two people appears twice.
39
66
  const kcsieRowsForPlacement = (placementId, placement, status) => {
40
- const base = { placementId, providerContactId: placement.providerContactId, studentForename: placement.studentForename, studentSurname: placement.studentSurname, employerName: placement.name, startDate: placement.startDate, endDate: placement.endDate };
67
+ const base = { placementId, providerContactId: placement.providerContactId, parentIsEmployer: placement.parentIsEmployer, studentForename: placement.studentForename, studentSurname: placement.studentSurname, employerName: placement.name, startDate: placement.startDate, endDate: placement.endDate };
41
68
  const rows = placement.kcsie === "notRequested" ?
42
69
  [{ ...base, status: "notRequested" }] :
43
70
  Object.entries(placement.kcsiePeople || {}).map(([personId, person]) => ({ ...base, status: person.status, personId, person }));
@@ -47,7 +74,7 @@ exports.kcsieRowsForPlacement = kcsieRowsForPlacement;
47
74
  // Applies the outcome to every placement at the school with this employer naming the person, and saves it on the employer for future placements.
48
75
  const reviewKcsiePerson = async ({ oId, providerContactId, personId, dob, previousSurnames, status, note, reviewedBy, reviewedByName }) => {
49
76
  const now = new Date();
50
- const outcome = { status, academicYear: (0, benchmarkEvidenceRules_1.academicYearForDate)(now), reviewed: now.toISOString(), reviewedBy, reviewedByName, ...(note?.trim() ? { note: note.trim() } : {}) };
77
+ const outcome = { status, reviewed: now.toISOString(), reviewedBy, reviewedByName, ...(note?.trim() ? { note: note.trim() } : {}) };
51
78
  await firebaseQuery.update(["providerContacts", providerContactId], { [`savedBy.${oId}.kcsie.${personId}`]: { ...outcome, ...(previousSurnames?.length ? { previousSurnames } : {}) } });
52
79
  const placements = await firebaseQuery.getDocsWhere(["placements"], [
53
80
  (0, firestore_1.where)("oId", "==", oId),
package/lib/kcsie.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"kcsie.js","sourceRoot":"","sources":["../src/kcsie.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kcsie.js","sourceRoot":"","sources":["../src/kcsie.ts"],"names":[],"mappings":"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"}
package/lib/kcsie.test.js CHANGED
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  jest.mock("firebase/firestore", () => ({ where: jest.fn() }));
4
4
  jest.mock("./firebase/firebaseQuery", () => jest.fn());
5
5
  const kcsie_1 = require("./kcsie");
6
- const person = (status, surname = "Smith", academicYear = "2026-27") => ({
7
- forename: "Jane", surname, dob: "1980-02-03", status, academicYear, submitted: "2026-09-01T00:00:00.000Z",
6
+ const person = (status, surname = "Smith", extra = {}) => ({
7
+ forename: "Jane", surname, dob: "1980-02-03", status, submitted: "2026-09-01T00:00:00.000Z", ...extra,
8
8
  });
9
9
  describe("kcsiePersonId", () => {
10
10
  it("matches the same surname and DOB however it is typed", () => {
@@ -12,11 +12,27 @@ describe("kcsiePersonId", () => {
12
12
  expect((0, kcsie_1.kcsiePersonId)({ surname: "Smith", dob: "1980-02-03" })).not.toBe((0, kcsie_1.kcsiePersonId)({ surname: "Smith", dob: "1981-02-03" }));
13
13
  });
14
14
  });
15
- describe("currentKcsieStatus", () => {
16
- const date = new Date("2026-10-08T12:00:00Z");
17
- it("keeps this year's outcome and resets last year's to pending", () => {
18
- expect((0, kcsie_1.currentKcsieStatus)({ status: "approved", academicYear: "2026-27" }, date)).toBe("approved");
19
- expect((0, kcsie_1.currentKcsieStatus)({ status: "rejected", academicYear: "2025-26" }, date)).toBe("pending");
15
+ describe("kcsieCheckedOverAYearAgo", () => {
16
+ const now = new Date("2026-10-08T12:00:00Z");
17
+ it("flags outcomes over a year old without resetting them", () => {
18
+ expect((0, kcsie_1.kcsieCheckedOverAYearAgo)(person("approved", "Smith", { reviewed: "2025-09-01T00:00:00Z" }), now)).toBe(true);
19
+ expect((0, kcsie_1.kcsieCheckedOverAYearAgo)(person("approved", "Smith", { reviewed: "2026-01-01T00:00:00Z" }), now)).toBe(false);
20
+ expect((0, kcsie_1.kcsieCheckedOverAYearAgo)(person("pending", "Smith", { reviewed: "2025-09-01T00:00:00Z" }), now)).toBe(false);
21
+ });
22
+ });
23
+ describe("kcsiePeopleDetails", () => {
24
+ it("imports names and DOBs from another placement, not its outcomes", () => {
25
+ expect((0, kcsie_1.kcsiePeopleDetails)({ smith: person("approved", "Smith", { previousSurnames: ["Jones"], reviewedBy: "uid1" }) }))
26
+ .toEqual([{ forename: "Jane", surname: "Smith", dob: "1980-02-03", previousSurnames: ["Jones"] }]);
27
+ expect((0, kcsie_1.kcsiePeopleDetails)(undefined)).toEqual([]);
28
+ });
29
+ });
30
+ describe("matchingKcsiePeople", () => {
31
+ const previous = [{ forename: "Jane", surname: "Smith", dob: "1980-02-03" }, { forename: "Bob", surname: "Smythe", dob: "1975-05-05" }, { forename: "Al", surname: "Lee", dob: "1990-01-01" }];
32
+ it("suggests previous individuals by surname once two letters are typed", () => {
33
+ expect((0, kcsie_1.matchingKcsiePeople)(previous, "S")).toEqual([]);
34
+ expect((0, kcsie_1.matchingKcsiePeople)(previous, "sm").map((p) => p.forename)).toEqual(["Jane", "Bob"]);
35
+ expect((0, kcsie_1.matchingKcsiePeople)(previous, "Smi").map((p) => p.forename)).toEqual(["Jane"]);
20
36
  });
21
37
  });
22
38
  describe("kcsieOutstanding", () => {
@@ -30,6 +46,42 @@ describe("workflowRequiresKcsie", () => {
30
46
  expect((0, kcsie_1.workflowRequiresKcsie)([{ id: 3, name: "Employer", userType: "Provider", kcsie: true }])).toBe(true);
31
47
  expect((0, kcsie_1.workflowRequiresKcsie)([{ id: 2, name: "Staff", userType: "Staff", kcsie: true }])).toBe(false);
32
48
  });
49
+ it("exempts parent employers unless the stage includes them", () => {
50
+ expect((0, kcsie_1.workflowRequiresKcsie)([{ id: 3, name: "Employer", userType: "Provider", kcsie: true }], { parentIsEmployer: true })).toBe(false);
51
+ expect((0, kcsie_1.workflowRequiresKcsie)([{ id: 3, name: "Employer", userType: "Provider", kcsie: true, kcsieIncludeParentEmployers: true }], { parentIsEmployer: true })).toBe(true);
52
+ });
53
+ });
54
+ describe("stageRequiresKcsie", () => {
55
+ it("needs the stage to ask about KCSIE", () => {
56
+ expect((0, kcsie_1.stageRequiresKcsie)({ kcsie: false }, { parentIsEmployer: false })).toBe(false);
57
+ expect((0, kcsie_1.stageRequiresKcsie)({ kcsie: true }, { parentIsEmployer: false })).toBe(true);
58
+ expect((0, kcsie_1.stageRequiresKcsie)({ kcsie: true })).toBe(true);
59
+ });
60
+ it("exempts parent employers by default", () => {
61
+ expect((0, kcsie_1.stageRequiresKcsie)({ kcsie: true }, { parentIsEmployer: true })).toBe(false);
62
+ expect((0, kcsie_1.stageRequiresKcsie)({ kcsie: true, kcsieIncludeParentEmployers: true }, { parentIsEmployer: true })).toBe(true);
63
+ });
64
+ });
65
+ describe("kcsieExemptionUpdate", () => {
66
+ const workflow = [{ id: 3, name: "Employer", userType: "Provider", kcsie: true }];
67
+ it("leaves placements alone when the workflow doesn't ask about KCSIE", () => {
68
+ expect((0, kcsie_1.kcsieExemptionUpdate)([{ id: 3, name: "Employer", userType: "Provider" }], { parentIsEmployer: true })).toBeUndefined();
69
+ expect((0, kcsie_1.kcsieExemptionUpdate)(undefined, {})).toBeUndefined();
70
+ });
71
+ it("marks exempt parent employers so they aren't counted as not yet requested", () => {
72
+ expect((0, kcsie_1.kcsieExemptionUpdate)(workflow, { parentIsEmployer: true })).toEqual(kcsie_1.KCSIE_PARENT_EXEMPT);
73
+ expect((0, kcsie_1.kcsieExemptionUpdate)(workflow, { parentIsEmployer: true, kcsie: "notRequested" })).toEqual(kcsie_1.KCSIE_PARENT_EXEMPT);
74
+ });
75
+ it("asks once the employer is no longer an exempt parent", () => {
76
+ expect((0, kcsie_1.kcsieExemptionUpdate)(workflow, {})).toEqual(kcsie_1.KCSIE_NOT_REQUESTED);
77
+ expect((0, kcsie_1.kcsieExemptionUpdate)(workflow, { parentIsEmployer: false, kcsie: "parentExempt" })).toEqual(kcsie_1.KCSIE_NOT_REQUESTED);
78
+ expect((0, kcsie_1.kcsieExemptionUpdate)([{ ...workflow[0], kcsieIncludeParentEmployers: true }], { parentIsEmployer: true, kcsie: "parentExempt" })).toEqual(kcsie_1.KCSIE_NOT_REQUESTED);
79
+ });
80
+ it("keeps an answer the employer has already given", () => {
81
+ expect((0, kcsie_1.kcsieExemptionUpdate)(workflow, { parentIsEmployer: true, kcsie: "pending" })).toBeUndefined();
82
+ expect((0, kcsie_1.kcsieExemptionUpdate)(workflow, { parentIsEmployer: false, kcsie: "approved" })).toBeUndefined();
83
+ expect((0, kcsie_1.kcsieExemptionUpdate)(workflow, { parentIsEmployer: false, kcsie: "notRequested" })).toBeUndefined();
84
+ });
33
85
  });
34
86
  describe("placementKcsieFields", () => {
35
87
  it("is not required when nobody was named", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"kcsie.test.js","sourceRoot":"","sources":["../src/kcsie.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kcsie.test.js","sourceRoot":"","sources":["../src/kcsie.test.ts"],"names":[],"mappings":";;AAAA,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC,CAAC;AAC5D,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;AAGvD,mCAAmR;AAInR,MAAM,MAAM,GAAG,CAAC,MAA6B,EAAE,OAAO,GAAG,OAAO,EAAE,QAA8B,EAAE,EAAe,EAAE,CAAC,CAAC;IACjH,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,0BAA0B,EAAE,GAAG,KAAK;CACxG,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,IAAA,qBAAa,EAAC,EAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAA,qBAAa,EAAC,EAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAC,CAAC,CAAC,CAAC;QAC7H,MAAM,CAAC,IAAA,qBAAa,EAAC,EAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAA,qBAAa,EAAC,EAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAC,CAAC,CAAC,CAAC;IAChI,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACtC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC7C,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,IAAA,gCAAwB,EAAC,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,EAAC,QAAQ,EAAE,sBAAsB,EAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClH,MAAM,CAAC,IAAA,gCAAwB,EAAC,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,EAAC,QAAQ,EAAE,sBAAsB,EAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnH,MAAM,CAAC,IAAA,gCAAwB,EAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,EAAC,QAAQ,EAAE,sBAAsB,EAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtH,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,IAAA,0BAAkB,EAAC,EAAC,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,EAAC,gBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,EAAC,CAAC,EAAC,CAAC,CAAC;aAC9G,OAAO,CAAC,CAAC,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC,OAAO,CAAC,EAAC,CAAC,CAAC,CAAC;QACrG,MAAM,CAAC,IAAA,0BAAkB,EAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACjC,MAAM,QAAQ,GAAG,CAAC,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAC,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,EAAC,CAAC,CAAC;IACzL,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC3E,MAAM,CAAC,IAAA,2BAAmB,EAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,IAAA,2BAAmB,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5F,MAAM,CAAC,IAAA,2BAAmB,EAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,wBAAgB,EAAC,CAAkC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzH,MAAM,CAAC,CAAC,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,wBAAgB,EAAC,CAAkC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjJ,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,IAAA,6BAAqB,EAAC,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzG,MAAM,CAAC,IAAA,6BAAqB,EAAC,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,IAAA,6BAAqB,EAAC,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpI,MAAM,CAAC,IAAA,6BAAqB,EAAC,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,IAAI,EAAC,CAAC,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1K,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,IAAA,0BAAkB,EAAC,EAAC,KAAK,EAAE,KAAK,EAAC,EAAE,EAAC,gBAAgB,EAAE,KAAK,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClF,MAAM,CAAC,IAAA,0BAAkB,EAAC,EAAC,KAAK,EAAE,IAAI,EAAC,EAAE,EAAC,gBAAgB,EAAE,KAAK,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChF,MAAM,CAAC,IAAA,0BAAkB,EAAC,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,IAAA,0BAAkB,EAAC,EAAC,KAAK,EAAE,IAAI,EAAC,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChF,MAAM,CAAC,IAAA,0BAAkB,EAAC,EAAC,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,IAAI,EAAC,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtH,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IAClC,MAAM,QAAQ,GAAG,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAmB,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC;IAEzF,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QACzE,MAAM,CAAC,IAAA,4BAAoB,EAAC,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAC,CAAC,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1H,MAAM,CAAC,IAAA,4BAAoB,EAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACjF,MAAM,CAAC,IAAA,4BAAoB,EAAC,QAAQ,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,OAAO,CAAC,2BAAmB,CAAC,CAAC;QAC9F,MAAM,CAAC,IAAA,4BAAoB,EAAC,QAAQ,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAC,CAAC,CAAC,CAAC,OAAO,CAAC,2BAAmB,CAAC,CAAC;IACzH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,IAAA,4BAAoB,EAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,2BAAmB,CAAC,CAAC;QACxE,MAAM,CAAC,IAAA,4BAAoB,EAAC,QAAQ,EAAE,EAAC,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAC,CAAC,CAAC,CAAC,OAAO,CAAC,2BAAmB,CAAC,CAAC;QACtH,MAAM,CAAC,IAAA,4BAAoB,EAAC,CAAC,EAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,2BAA2B,EAAE,IAAI,EAAC,CAAC,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAC,CAAC,CAAC,CAAC,OAAO,CAAC,2BAAmB,CAAC,CAAC;IACtK,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,IAAA,4BAAoB,EAAC,QAAQ,EAAE,EAAC,gBAAgB,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACnG,MAAM,CAAC,IAAA,4BAAoB,EAAC,QAAQ,EAAE,EAAC,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACrG,MAAM,CAAC,IAAA,4BAAoB,EAAC,QAAQ,EAAE,EAAC,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7G,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,IAAA,4BAAoB,EAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAC,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,EAAC,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,IAAA,4BAAoB,EAAC,EAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,EAAC,CAAC,CAAC;aAC7F,OAAO,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,EAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACnC,MAAM,SAAS,GAAG,CAAC,KAAoC,EAAE,EAAE,CAAC,CAAC,EAAC,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,KAAK,EAAC,CAAyB,CAAC;IACtK,MAAM,MAAM,GAAG,EAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,EAAC,CAAC;IAE1E,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,IAAA,6BAAqB,EAAC,IAAI,EAAE,SAAS,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;QAChL,MAAM,CAAC,IAAA,6BAAqB,EAAC,IAAI,EAAE,SAAS,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAC,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/I,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,IAAA,6BAAqB,EAAC,IAAI,EAAE,SAAS,CAAC,EAAC,KAAK,EAAE,cAAc,EAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IAC3H,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -135,11 +135,10 @@ export type StudentPlacementData = {
135
135
  dbsCheckData?: {
136
136
  [key: string]: unknown;
137
137
  }[] | string;
138
- kcsie?: "pending" | "approved" | "flagged" | "rejected" | "notRequired" | "notRequested";
138
+ kcsie?: "pending" | "approved" | "flagged" | "rejected" | "notRequired" | "notRequested" | "parentExempt";
139
139
  kcsiePeople?: {
140
140
  [personId: string]: NonNullable<PlacementQuestions["kcsie"]>["people"][number] & {
141
141
  status: "pending" | "approved" | "flagged" | "rejected";
142
- academicYear: string;
143
142
  submitted: string;
144
143
  reviewed?: string;
145
144
  reviewedBy?: string;
@@ -938,6 +937,7 @@ export type WorkflowStage = {
938
937
  riskAssessment?: boolean;
939
938
  dbsCheck?: boolean;
940
939
  kcsie?: boolean;
940
+ kcsieIncludeParentEmployers?: boolean;
941
941
  defaultForms?: {
942
942
  name: string;
943
943
  description: string;
@@ -1087,7 +1087,7 @@ export type ProviderContactData = {
1087
1087
  [key: string]: unknown;
1088
1088
  };
1089
1089
  kcsie?: {
1090
- [personId: string]: Pick<NonNullable<StudentPlacementData["kcsiePeople"]>[string], "status" | "academicYear" | "reviewed" | "reviewedBy" | "reviewedByName" | "note" | "previousSurnames">;
1090
+ [personId: string]: Pick<NonNullable<StudentPlacementData["kcsiePeople"]>[string], "status" | "reviewed" | "reviewedBy" | "reviewedByName" | "note" | "previousSurnames">;
1091
1091
  };
1092
1092
  status: "uploaded" | "approved" | "rejected" | "providerReviewed" | "blocked";
1093
1093
  };
@@ -1678,6 +1678,7 @@ export type PlacementReviewDetails = {
1678
1678
  dbsCheckData?: {
1679
1679
  [key: string]: unknown;
1680
1680
  } | string;
1681
+ kcsiePeople?: NonNullable<PlacementQuestions["kcsie"]>["people"];
1681
1682
  };
1682
1683
  };
1683
1684
  requireELI?: boolean;
@@ -1685,6 +1686,7 @@ export type PlacementReviewDetails = {
1685
1686
  requireDBS?: boolean;
1686
1687
  requireKCSIE?: boolean;
1687
1688
  kcsiePeople?: NonNullable<PlacementQuestions["kcsie"]>["people"];
1689
+ kcsiePrevious?: NonNullable<PlacementQuestions["kcsie"]>["people"];
1688
1690
  kcsie?: StudentPlacementData["kcsie"];
1689
1691
  importedProviderForms: boolean;
1690
1692
  currentStage: WorkflowStage & {
@@ -1 +1 @@
1
- {"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;AAusIA,sDASC;AAeD,oDAKC;AAaD,0CAEC;AA9pED;;;;;kFAKkF;AACrE,QAAA,qBAAqB,GAAG,SAAS,CAAC;AAClC,QAAA,iBAAiB,GAAG,OAAO,CAAC;AA0kEzC;yEACyE;AAC5D,QAAA,0BAA0B,GAA2C;IAC9E,gBAAgB,EAAE,2BAA2B;IAC7C,mBAAmB,EAAE,sBAAsB;IAC3C,cAAc,EAAE,iBAAiB;IACjC,kBAAkB,EAAE,qBAAqB;IACzC,UAAU,EAAE,oCAAoC;IAChD,aAAa,EAAE,yBAAyB;IACxC,aAAa,EAAE,wBAAwB;IACvC,KAAK,EAAE,OAAO;CACjB,CAAC;AAEW,QAAA,0BAA0B,GAA+C;IAClF,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,SAAgB,qBAAqB,CAAC,SAAsC;IACxE,QAAQ,SAAS,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC;QAC5B,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,KAAK,WAAW,CAAC;QACjB,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;QACb,OAAO,CAAC,CAAC,OAAO,UAAU,CAAC;IAC3B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,oBAAoB,CAChC,IAAwC,EACxC,MAA6C;IAE7C,OAAO,IAAI,EAAE,WAAW,IAAI,MAAM,EAAE,YAAY,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,eAAe,CAAC,KAA6D;IACzF,OAAO,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC"}
1
+ {"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;AA4sIA,sDASC;AAeD,oDAKC;AAaD,0CAEC;AA9pED;;;;;kFAKkF;AACrE,QAAA,qBAAqB,GAAG,SAAS,CAAC;AAClC,QAAA,iBAAiB,GAAG,OAAO,CAAC;AA0kEzC;yEACyE;AAC5D,QAAA,0BAA0B,GAA2C;IAC9E,gBAAgB,EAAE,2BAA2B;IAC7C,mBAAmB,EAAE,sBAAsB;IAC3C,cAAc,EAAE,iBAAiB;IACjC,kBAAkB,EAAE,qBAAqB;IACzC,UAAU,EAAE,oCAAoC;IAChD,aAAa,EAAE,yBAAyB;IACxC,aAAa,EAAE,wBAAwB;IACvC,KAAK,EAAE,OAAO;CACjB,CAAC;AAEW,QAAA,0BAA0B,GAA+C;IAClF,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,SAAgB,qBAAqB,CAAC,SAAsC;IACxE,QAAQ,SAAS,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC;QAC5B,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,KAAK,WAAW,CAAC;QACjB,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;QACb,OAAO,CAAC,CAAC,OAAO,UAAU,CAAC;IAC3B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,oBAAoB,CAChC,IAAwC,EACxC,MAA6C;IAE7C,OAAO,IAAI,EAAE,WAAW,IAAI,MAAM,EAAE,YAAY,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,eAAe,CAAC,KAA6D;IACzF,OAAO,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "private": false,
3
3
  "name": "placementt-core",
4
4
  "author": "Placementt",
5
- "version": "1.400.1187",
5
+ "version": "1.400.1189",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "scripts": {
package/src/kcsie.test.ts CHANGED
@@ -2,12 +2,12 @@ jest.mock("firebase/firestore", () => ({where: jest.fn()}));
2
2
  jest.mock("./firebase/firebaseQuery", () => jest.fn());
3
3
 
4
4
  import {StudentPlacementData} from "./typeDefinitions";
5
- import {currentKcsieStatus, kcsieOutstanding, kcsiePersonId, kcsieRowsForPlacement, placementKcsieFields, workflowRequiresKcsie} from "./kcsie";
5
+ import {KCSIE_NOT_REQUESTED, KCSIE_PARENT_EXEMPT, kcsieExemptionUpdate, stageRequiresKcsie, kcsieCheckedOverAYearAgo, kcsieOutstanding, kcsiePeopleDetails, matchingKcsiePeople, kcsiePersonId, kcsieRowsForPlacement, placementKcsieFields, workflowRequiresKcsie} from "./kcsie";
6
6
 
7
7
  type KcsiePerson = NonNullable<StudentPlacementData["kcsiePeople"]>[string];
8
8
 
9
- const person = (status: KcsiePerson["status"], surname = "Smith", academicYear = "2026-27"): KcsiePerson => ({
10
- forename: "Jane", surname, dob: "1980-02-03", status, academicYear, submitted: "2026-09-01T00:00:00.000Z",
9
+ const person = (status: KcsiePerson["status"], surname = "Smith", extra: Partial<KcsiePerson> = {}): KcsiePerson => ({
10
+ forename: "Jane", surname, dob: "1980-02-03", status, submitted: "2026-09-01T00:00:00.000Z", ...extra,
11
11
  });
12
12
 
13
13
  describe("kcsiePersonId", () => {
@@ -17,11 +17,29 @@ describe("kcsiePersonId", () => {
17
17
  });
18
18
  });
19
19
 
20
- describe("currentKcsieStatus", () => {
21
- const date = new Date("2026-10-08T12:00:00Z");
22
- it("keeps this year's outcome and resets last year's to pending", () => {
23
- expect(currentKcsieStatus({status: "approved", academicYear: "2026-27"}, date)).toBe("approved");
24
- expect(currentKcsieStatus({status: "rejected", academicYear: "2025-26"}, date)).toBe("pending");
20
+ describe("kcsieCheckedOverAYearAgo", () => {
21
+ const now = new Date("2026-10-08T12:00:00Z");
22
+ it("flags outcomes over a year old without resetting them", () => {
23
+ expect(kcsieCheckedOverAYearAgo(person("approved", "Smith", {reviewed: "2025-09-01T00:00:00Z"}), now)).toBe(true);
24
+ expect(kcsieCheckedOverAYearAgo(person("approved", "Smith", {reviewed: "2026-01-01T00:00:00Z"}), now)).toBe(false);
25
+ expect(kcsieCheckedOverAYearAgo(person("pending", "Smith", {reviewed: "2025-09-01T00:00:00Z"}), now)).toBe(false);
26
+ });
27
+ });
28
+
29
+ describe("kcsiePeopleDetails", () => {
30
+ it("imports names and DOBs from another placement, not its outcomes", () => {
31
+ expect(kcsiePeopleDetails({smith: person("approved", "Smith", {previousSurnames: ["Jones"], reviewedBy: "uid1"})}))
32
+ .toEqual([{forename: "Jane", surname: "Smith", dob: "1980-02-03", previousSurnames: ["Jones"]}]);
33
+ expect(kcsiePeopleDetails(undefined)).toEqual([]);
34
+ });
35
+ });
36
+
37
+ describe("matchingKcsiePeople", () => {
38
+ const previous = [{forename: "Jane", surname: "Smith", dob: "1980-02-03"}, {forename: "Bob", surname: "Smythe", dob: "1975-05-05"}, {forename: "Al", surname: "Lee", dob: "1990-01-01"}];
39
+ it("suggests previous individuals by surname once two letters are typed", () => {
40
+ expect(matchingKcsiePeople(previous, "S")).toEqual([]);
41
+ expect(matchingKcsiePeople(previous, "sm").map((p) => p.forename)).toEqual(["Jane", "Bob"]);
42
+ expect(matchingKcsiePeople(previous, "Smi").map((p) => p.forename)).toEqual(["Jane"]);
25
43
  });
26
44
  });
27
45
 
@@ -37,6 +55,50 @@ describe("workflowRequiresKcsie", () => {
37
55
  expect(workflowRequiresKcsie([{id: 3, name: "Employer", userType: "Provider", kcsie: true}])).toBe(true);
38
56
  expect(workflowRequiresKcsie([{id: 2, name: "Staff", userType: "Staff", kcsie: true}])).toBe(false);
39
57
  });
58
+
59
+ it("exempts parent employers unless the stage includes them", () => {
60
+ expect(workflowRequiresKcsie([{id: 3, name: "Employer", userType: "Provider", kcsie: true}], {parentIsEmployer: true})).toBe(false);
61
+ expect(workflowRequiresKcsie([{id: 3, name: "Employer", userType: "Provider", kcsie: true, kcsieIncludeParentEmployers: true}], {parentIsEmployer: true})).toBe(true);
62
+ });
63
+ });
64
+
65
+ describe("stageRequiresKcsie", () => {
66
+ it("needs the stage to ask about KCSIE", () => {
67
+ expect(stageRequiresKcsie({kcsie: false}, {parentIsEmployer: false})).toBe(false);
68
+ expect(stageRequiresKcsie({kcsie: true}, {parentIsEmployer: false})).toBe(true);
69
+ expect(stageRequiresKcsie({kcsie: true})).toBe(true);
70
+ });
71
+
72
+ it("exempts parent employers by default", () => {
73
+ expect(stageRequiresKcsie({kcsie: true}, {parentIsEmployer: true})).toBe(false);
74
+ expect(stageRequiresKcsie({kcsie: true, kcsieIncludeParentEmployers: true}, {parentIsEmployer: true})).toBe(true);
75
+ });
76
+ });
77
+
78
+ describe("kcsieExemptionUpdate", () => {
79
+ const workflow = [{id: 3, name: "Employer", userType: "Provider" as const, kcsie: true}];
80
+
81
+ it("leaves placements alone when the workflow doesn't ask about KCSIE", () => {
82
+ expect(kcsieExemptionUpdate([{id: 3, name: "Employer", userType: "Provider"}], {parentIsEmployer: true})).toBeUndefined();
83
+ expect(kcsieExemptionUpdate(undefined, {})).toBeUndefined();
84
+ });
85
+
86
+ it("marks exempt parent employers so they aren't counted as not yet requested", () => {
87
+ expect(kcsieExemptionUpdate(workflow, {parentIsEmployer: true})).toEqual(KCSIE_PARENT_EXEMPT);
88
+ expect(kcsieExemptionUpdate(workflow, {parentIsEmployer: true, kcsie: "notRequested"})).toEqual(KCSIE_PARENT_EXEMPT);
89
+ });
90
+
91
+ it("asks once the employer is no longer an exempt parent", () => {
92
+ expect(kcsieExemptionUpdate(workflow, {})).toEqual(KCSIE_NOT_REQUESTED);
93
+ expect(kcsieExemptionUpdate(workflow, {parentIsEmployer: false, kcsie: "parentExempt"})).toEqual(KCSIE_NOT_REQUESTED);
94
+ expect(kcsieExemptionUpdate([{...workflow[0], kcsieIncludeParentEmployers: true}], {parentIsEmployer: true, kcsie: "parentExempt"})).toEqual(KCSIE_NOT_REQUESTED);
95
+ });
96
+
97
+ it("keeps an answer the employer has already given", () => {
98
+ expect(kcsieExemptionUpdate(workflow, {parentIsEmployer: true, kcsie: "pending"})).toBeUndefined();
99
+ expect(kcsieExemptionUpdate(workflow, {parentIsEmployer: false, kcsie: "approved"})).toBeUndefined();
100
+ expect(kcsieExemptionUpdate(workflow, {parentIsEmployer: false, kcsie: "notRequested"})).toBeUndefined();
101
+ });
40
102
  });
41
103
 
42
104
  describe("placementKcsieFields", () => {
package/src/kcsie.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import {where} from "firebase/firestore";
2
- import {academicYearForDate} from "./benchmarkEvidenceRules";
3
2
  import FirebaseQuery from "./firebase/firebaseQuery";
4
- import {StudentPlacementData, WorkflowStage} from "./typeDefinitions";
3
+ import {PlacementQuestions, StudentPlacementData, WorkflowStage} from "./typeDefinitions";
5
4
 
6
5
  const firebaseQuery = new FirebaseQuery();
7
6
 
@@ -16,23 +15,51 @@ export const KCSIE_STATUS_LABELS: Record<NonNullable<StudentPlacementData["kcsie
16
15
  rejected: "Rejected",
17
16
  notRequired: "Not required",
18
17
  notRequested: "Not yet requested",
18
+ parentExempt: "Exempt: employer is parent",
19
19
  };
20
20
 
21
21
  export const KCSIE_NOT_REQUESTED: Pick<StudentPlacementData, "kcsie"|"kcsieStatuses"> = {kcsie: "notRequested", kcsieStatuses: ["notRequested"]};
22
22
 
23
+ export const KCSIE_PARENT_EXEMPT: Pick<StudentPlacementData, "kcsie"|"kcsieStatuses"> = {kcsie: "parentExempt", kcsieStatuses: ["parentExempt"]};
24
+
23
25
  // Surname and DOB are what the DfE search uses, so they identify a person across placements.
24
26
  export const kcsiePersonId = ({surname, dob}: Pick<KcsiePerson, "surname"|"dob">): string =>
25
27
  `${surname.trim().toLowerCase().replace(/[^a-z]/g, "")}_${dob}`;
26
28
 
27
- // Outcomes from an earlier academic year need checking again.
28
- export const currentKcsieStatus = (person: Pick<KcsiePerson, "status"|"academicYear">, date = new Date()): KcsiePerson["status"] =>
29
- person.academicYear === academicYearForDate(date) ? person.status : "pending";
29
+ // Outcomes carry over indefinitely; the school is just told when one is over a year old.
30
+ export const kcsieCheckedOverAYearAgo = (person: Pick<KcsiePerson, "status"|"reviewed">, now = new Date()): boolean =>
31
+ person.status !== "pending" && Boolean(person.reviewed) && now.getTime() - new Date(person.reviewed as string).getTime() > 365 * 24 * 60 * 60 * 1000;
32
+
33
+ // Just the details the employer enters, e.g. to import people from another placement.
34
+ export const kcsiePeopleDetails = (people?: StudentPlacementData["kcsiePeople"]): NonNullable<PlacementQuestions["kcsie"]>["people"] =>
35
+ Object.values(people || {}).map(({forename, surname, dob, previousSurnames}) => ({forename, surname, dob, ...(previousSurnames?.length ? {previousSurnames} : {})}));
36
+
37
+ // Previous individuals whose surname starts with what's been typed, for the employer to pick from.
38
+ export const matchingKcsiePeople = (previous: NonNullable<PlacementQuestions["kcsie"]>["people"], surname: string): NonNullable<PlacementQuestions["kcsie"]>["people"] => {
39
+ const typed = surname.trim().toLowerCase();
40
+ return typed.length < 2 ? [] : previous.filter((p) => p.surname.toLowerCase().startsWith(typed));
41
+ };
30
42
 
31
43
  export const kcsieOutstanding = (status?: StudentPlacementData["kcsie"]): boolean =>
32
44
  status === "pending" || status === "flagged" || status === "rejected";
33
45
 
34
- export const workflowRequiresKcsie = (workflow?: WorkflowStage[]): boolean =>
35
- Boolean(workflow?.some((s) => s.kcsie && s.userType === "Provider"));
46
+ // Parent employers are exempt unless the stage opts them in.
47
+ export const stageRequiresKcsie = (stage: Pick<WorkflowStage, "kcsie"|"kcsieIncludeParentEmployers">, placement?: Pick<StudentPlacementData, "parentIsEmployer">): boolean =>
48
+ Boolean(stage.kcsie) && !(placement?.parentIsEmployer && !stage.kcsieIncludeParentEmployers);
49
+
50
+ // Without a placement, whether any placement on the workflow could need a check.
51
+ export const workflowRequiresKcsie = (workflow?: WorkflowStage[], placement?: Pick<StudentPlacementData, "parentIsEmployer">): boolean =>
52
+ Boolean(workflow?.some((s) => s.userType === "Provider" && stageRequiresKcsie(s, placement)));
53
+
54
+ // The KCSIE fields to set once it's known whether the employer is an exempt parent, or undefined to leave them.
55
+ // Placements the employer has already answered for keep their answer.
56
+ export const kcsieExemptionUpdate = (workflow: WorkflowStage[]|undefined, placement: Pick<StudentPlacementData, "kcsie"|"parentIsEmployer">): Pick<StudentPlacementData, "kcsie"|"kcsieStatuses">|undefined => {
57
+ if (!workflowRequiresKcsie(workflow)) return undefined;
58
+ const exempt = !workflowRequiresKcsie(workflow, placement);
59
+ if (exempt && (!placement.kcsie || placement.kcsie === "notRequested")) return KCSIE_PARENT_EXEMPT;
60
+ if (!exempt && (!placement.kcsie || placement.kcsie === "parentExempt")) return KCSIE_NOT_REQUESTED;
61
+ return undefined;
62
+ };
36
63
 
37
64
  // The worst person's status wins, so one rejection holds the placement.
38
65
  export const placementKcsieFields = (people: NonNullable<StudentPlacementData["kcsiePeople"]>): Pick<StudentPlacementData, "kcsie"|"kcsieStatuses"> => {
@@ -43,7 +70,7 @@ export const placementKcsieFields = (people: NonNullable<StudentPlacementData["k
43
70
 
44
71
  // One row per person, so a placement naming two people appears twice.
45
72
  export const kcsieRowsForPlacement = (placementId: string, placement: StudentPlacementData, status?: StudentPlacementData["kcsie"]) => {
46
- const base = {placementId, providerContactId: placement.providerContactId, studentForename: placement.studentForename, studentSurname: placement.studentSurname, employerName: placement.name, startDate: placement.startDate, endDate: placement.endDate};
73
+ const base = {placementId, providerContactId: placement.providerContactId, parentIsEmployer: placement.parentIsEmployer, studentForename: placement.studentForename, studentSurname: placement.studentSurname, employerName: placement.name, startDate: placement.startDate, endDate: placement.endDate};
47
74
  const rows: (typeof base & {status: StudentPlacementData["kcsie"], personId?: string, person?: KcsiePerson})[] = placement.kcsie === "notRequested" ?
48
75
  [{...base, status: "notRequested"}] :
49
76
  Object.entries(placement.kcsiePeople || {}).map(([personId, person]) => ({...base, status: person.status, personId, person}));
@@ -53,7 +80,7 @@ export const kcsieRowsForPlacement = (placementId: string, placement: StudentPla
53
80
  // Applies the outcome to every placement at the school with this employer naming the person, and saves it on the employer for future placements.
54
81
  export const reviewKcsiePerson = async ({oId, providerContactId, personId, dob, previousSurnames, status, note, reviewedBy, reviewedByName}: {oId: string, providerContactId: string, personId: string, dob: string, previousSurnames?: string[], status: KcsiePerson["status"], note?: string, reviewedBy: string, reviewedByName: string}): Promise<void> => {
55
82
  const now = new Date();
56
- const outcome = {status, academicYear: academicYearForDate(now), reviewed: now.toISOString(), reviewedBy, reviewedByName, ...(note?.trim() ? {note: note.trim()} : {})};
83
+ const outcome = {status, reviewed: now.toISOString(), reviewedBy, reviewedByName, ...(note?.trim() ? {note: note.trim()} : {})};
57
84
 
58
85
  await firebaseQuery.update(["providerContacts", providerContactId], {[`savedBy.${oId}.kcsie.${personId}`]: {...outcome, ...(previousSurnames?.length ? {previousSurnames} : {})}});
59
86
 
@@ -123,11 +123,11 @@ export type StudentPlacementData = {
123
123
  dbsCheck?: boolean|"awaitingReview",
124
124
  dbsCheckType?: "file"|"form"|"skipped",
125
125
  dbsCheckData?: {[key:string]: unknown}[]|string,
126
- kcsie?: "pending"|"approved"|"flagged"|"rejected"|"notRequired"|"notRequested",
126
+ // parentExempt: the employer is the student's parent and the workflow exempts them.
127
+ kcsie?: "pending"|"approved"|"flagged"|"rejected"|"notRequired"|"notRequested"|"parentExempt",
127
128
  // Keyed by kcsiePersonId (surname and DOB).
128
129
  kcsiePeople?: {[personId: string]: NonNullable<PlacementQuestions["kcsie"]>["people"][number] & {
129
130
  status: "pending"|"approved"|"flagged"|"rejected",
130
- academicYear: string,
131
131
  submitted: string,
132
132
  reviewed?: string,
133
133
  reviewedBy?: string, // uid
@@ -949,6 +949,8 @@ export type WorkflowStage = {
949
949
  riskAssessment?: boolean,
950
950
  dbsCheck?: boolean,
951
951
  kcsie?: boolean,
952
+ // Placements with the student's parent as employer are exempt from KCSIE unless this is set.
953
+ kcsieIncludeParentEmployers?: boolean,
952
954
  defaultForms?: {
953
955
  name: string,
954
956
  description: string,
@@ -1130,7 +1132,7 @@ export type ProviderContactData = {
1130
1132
  sharing?: EmployerSharing,
1131
1133
  additionalData?: {[key: string]: unknown},
1132
1134
  // This school's KCSIE outcomes, so new placements pick them up.
1133
- kcsie?: {[personId: string]: Pick<NonNullable<StudentPlacementData["kcsiePeople"]>[string], "status"|"academicYear"|"reviewed"|"reviewedBy"|"reviewedByName"|"note"|"previousSurnames">},
1135
+ kcsie?: {[personId: string]: Pick<NonNullable<StudentPlacementData["kcsiePeople"]>[string], "status"|"reviewed"|"reviewedBy"|"reviewedByName"|"note"|"previousSurnames">},
1134
1136
  status: "uploaded"|"approved"|"rejected"|"providerReviewed"|"blocked"}},
1135
1137
  }&Address
1136
1138
 
@@ -1706,6 +1708,7 @@ export type PlacementReviewDetails = {
1706
1708
  dbsCheck: boolean|"awaitingReview",
1707
1709
  dbsCheckType: "form"|"file",
1708
1710
  dbsCheckData?: {[key: string]: unknown}|string,
1711
+ kcsiePeople?: NonNullable<PlacementQuestions["kcsie"]>["people"],
1709
1712
  }
1710
1713
  },
1711
1714
  requireELI?: boolean,
@@ -1713,6 +1716,8 @@ export type PlacementReviewDetails = {
1713
1716
  requireDBS?: boolean,
1714
1717
  requireKCSIE?: boolean,
1715
1718
  kcsiePeople?: NonNullable<PlacementQuestions["kcsie"]>["people"],
1719
+ // Everyone the employer has named to this school before, offered as suggestions.
1720
+ kcsiePrevious?: NonNullable<PlacementQuestions["kcsie"]>["people"],
1716
1721
  kcsie?: StudentPlacementData["kcsie"],
1717
1722
  importedProviderForms: boolean,
1718
1723
  currentStage: WorkflowStage&{files: {[fileId: string]: {name: string, url: string}}},