Let advisors assign a new will amendment case to themselves
3c936ebfeat(amendments)merged 2d ago3 review threads
Illustrative: based on a real change, edited for clarity and privacy.
src/domains/cases/actions/create-amendment-case.ts+20 −5
| @@ const inputSchema = z.object({ | |||
| 26 | 26 | willBank: z.enum(WILL_BANK_NAMES).optional(), | |
| 27 | 27 | /** Triage summary attached as a NOTE activity on the new case. */ | |
| 28 | 28 | intakeNote: z.string().trim().min(1).max(3000).optional(), | |
| 29 | added | /** When true, assign the new case to the signed-in advisor. */ | |
| 1 line 30, review thread 1 | added | assignToMe: z.boolean().optional().default(false), | |
| 29 | 31 | }) | |
| 30 | 32 | ||
| 31 | removed | export type CreateAmendmentCaseInput = z.infer<typeof inputSchema> | |
| 2 line 33, review thread 2 | added | export type CreateAmendmentCaseInput = z.input<typeof inputSchema> | |
| 32 | 34 | ||
| 33 | 35 | export type CreateAmendmentCaseResult = | |
| 34 | removed | | { success: true; caseRef: string } | |
| 36 | added | | { success: true; caseRef: string; selfAssignFailed: boolean } | |
| 35 | 37 | | { success: false; error: string } | |
| 36 | 38 | ||
| 37 | 39 | const log = createLogger('cases', 'create-amendment-case') | |
| 38 | 40 | ||
| @@ export async function createAmendmentCase( | |||
| 71 | 89 | return { success: false, error: 'Case service is not configured' } | |
| 72 | 90 | } | |
| 73 | 91 | ||
| 74 | removed | const { customerRef, initialState, willBank, intakeNote, idempotencySession } = | |
| 75 | removed | parsed.data | |
| 92 | added | const { | |
| 93 | added | customerRef, | |
| 94 | added | initialState, | |
| 95 | added | willBank, | |
| 96 | added | intakeNote, | |
| 97 | added | idempotencySession, | |
| 98 | added | assignToMe, | |
| 99 | added | } = parsed.data | |
| 100 | added | const assignTo = assignToMe ? advisorId : undefined | |
| 76 | 101 | ||
| 77 | 102 | log.info('Creating amendment case', { | |
| 78 | 103 | advisorId, | |
| @@ export async function createAmendmentCase( | |||
| 96 | 121 | initial_state: initialState, | |
| 97 | 122 | data: { is_amendment_case: true }, | |
| 98 | 123 | ...(intakeNote ? { intake_note: intakeNote } : {}), | |
| 3 line 124, review thread 3 | added | ...(assignTo ? { assign_to: assignTo } : {}), | |
| 99 | 125 | } | |
| 100 | 126 | ||
| 101 | 127 | try { | |
| @@ export async function createAmendmentCase( | |||
| 117 | 143 | ||
| 118 | 144 | log.info('Amendment case created', { advisorId, caseRef }) | |
| 119 | 145 | emitMetric('AmendmentCaseCreated') | |
| 120 | removed | return { success: true, caseRef } | |
| 146 | added | return { | |
| 147 | added | success: true, | |
| 148 | added | caseRef, | |
| 149 | added | // The case exists either way; the modal says so if self-assignment didn't stick. | |
| 150 | added | selfAssignFailed: assignmentMissed(assignTo, response), | |
| 151 | added | } | |
| 121 | 152 | } catch (error) { | |
advisors team
Most amendments I triage I end up handling myself, so this saves a step every time. Could it be ticked by default?
author
Starting unticked so nobody picks up a case by accident. If most people tick it over the next couple of weeks, we’ll flip the default.
AI reviewP1
The TypeScript build fails
.default(false)makesassignToMerequired inz.infer, which describes the parsed output. The modal leaves the field out, so its call no longer type-checks. Usez.inputfor the request type and keep the default.author
Good spot. Switched to
z.input: callers can leave it out, and the action still gets a boolean.
reviewer
Adding
assign_tochanges the idempotency key. If the case saves but the response is lost, and the advisor ticks or unticks the box before retrying, we’d create a second case. Do we care?author
That needs the response to be lost in transit and the advisor to change their choice before retrying. It’s a very small edge case, so we’re not adding machinery for it.
reviewer
Agreed, both have to happen at once. Let’s note it on the ticket rather than build for it now. Approving.