feat(tasks): implement runGenerateAndAssign for advocate-designation mode
This commit is contained in:
parent
64f8b71603
commit
fd38e0c148
2 changed files with 452 additions and 1 deletions
|
|
@ -1,4 +1,144 @@
|
||||||
import { parseGenerateAndAssignTarget, GenerateAndAssignError } from '../generate-and-assign-core'
|
const mockUserFindUnique = jest.fn()
|
||||||
|
const mockClientFindMany = jest.fn()
|
||||||
|
const mockClientFindUnique = jest.fn()
|
||||||
|
const mockTaskTemplateFindMany = jest.fn()
|
||||||
|
const mockPolicyGroupFindMany = jest.fn()
|
||||||
|
const mockPolicyFindMany = jest.fn()
|
||||||
|
const mockTaskCreateMany = jest.fn().mockResolvedValue({ count: 0 })
|
||||||
|
const mockTaskCreate = jest.fn()
|
||||||
|
const mockTaskFindMany = jest.fn().mockResolvedValue([])
|
||||||
|
const mockTaskFindFirst = jest.fn().mockResolvedValue(null)
|
||||||
|
const mockTaskAssignmentCreateMany = jest.fn().mockResolvedValue({ count: 0 })
|
||||||
|
const mockTaskAssignmentCreate = jest.fn()
|
||||||
|
const mockAuditLogCreate = jest.fn().mockResolvedValue({})
|
||||||
|
|
||||||
|
jest.mock('@/lib/db', () => ({
|
||||||
|
prisma: {
|
||||||
|
user: { findUnique: (...args: any[]) => mockUserFindUnique(...args) },
|
||||||
|
client: {
|
||||||
|
findMany: (...args: any[]) => mockClientFindMany(...args),
|
||||||
|
findUnique: (...args: any[]) => mockClientFindUnique(...args),
|
||||||
|
},
|
||||||
|
taskTemplate: { findMany: (...args: any[]) => mockTaskTemplateFindMany(...args) },
|
||||||
|
policyGroup: { findMany: (...args: any[]) => mockPolicyGroupFindMany(...args) },
|
||||||
|
policy: { findMany: (...args: any[]) => mockPolicyFindMany(...args) },
|
||||||
|
task: {
|
||||||
|
createMany: (...args: any[]) => mockTaskCreateMany(...args),
|
||||||
|
create: (...args: any[]) => mockTaskCreate(...args),
|
||||||
|
findMany: (...args: any[]) => mockTaskFindMany(...args),
|
||||||
|
findFirst: (...args: any[]) => mockTaskFindFirst(...args),
|
||||||
|
},
|
||||||
|
taskAssignment: {
|
||||||
|
createMany: (...args: any[]) => mockTaskAssignmentCreateMany(...args),
|
||||||
|
create: (...args: any[]) => mockTaskAssignmentCreate(...args),
|
||||||
|
},
|
||||||
|
auditLog: { create: (...args: any[]) => mockAuditLogCreate(...args) },
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
import { runGenerateAndAssign, parseGenerateAndAssignTarget, GenerateAndAssignError } from '../generate-and-assign-core'
|
||||||
|
|
||||||
|
const now = new Date('2026-07-18T00:00:00Z')
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
jest.useFakeTimers().setSystemTime(now)
|
||||||
|
mockClientFindMany.mockResolvedValue([])
|
||||||
|
mockTaskTemplateFindMany.mockResolvedValue([])
|
||||||
|
mockPolicyGroupFindMany.mockResolvedValue([])
|
||||||
|
mockPolicyFindMany.mockResolvedValue([])
|
||||||
|
mockTaskCreateMany.mockResolvedValue({ count: 0 })
|
||||||
|
mockTaskFindMany.mockResolvedValue([])
|
||||||
|
mockTaskFindFirst.mockResolvedValue(null)
|
||||||
|
mockTaskAssignmentCreateMany.mockResolvedValue({ count: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('runGenerateAndAssign — advocate-designation mode', () => {
|
||||||
|
const target = { mode: 'advocate-designation' as const, advocateId: 'adv-1', designationId: 'des-1' }
|
||||||
|
|
||||||
|
// The implementation calls client.findMany twice: once to find clients
|
||||||
|
// matching the advocate+designation (select: { id }), and once at the end
|
||||||
|
// to look up display names for the summary (select: { id, name }). This
|
||||||
|
// mock serves both from one place, branching on which fields were selected.
|
||||||
|
const mockClientsForAdvocateDesignation = () => {
|
||||||
|
mockClientFindMany.mockImplementation((args: any) =>
|
||||||
|
args?.select?.name
|
||||||
|
? Promise.resolve([{ id: 'client-1', name: 'Client One' }])
|
||||||
|
: Promise.resolve([{ id: 'client-1' }])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
it('computes counts without writing when dryRun is true', async () => {
|
||||||
|
mockUserFindUnique.mockResolvedValue({ id: 'adv-1', displayName: 'Jane Smith', isActive: true })
|
||||||
|
mockClientsForAdvocateDesignation()
|
||||||
|
mockTaskTemplateFindMany.mockResolvedValue([
|
||||||
|
{
|
||||||
|
id: 'template-1',
|
||||||
|
name: 'Prepare loss summary',
|
||||||
|
description: null,
|
||||||
|
department: 'Commercial Lines',
|
||||||
|
timing: 'PRE_RENEWAL',
|
||||||
|
daysOffset: -1,
|
||||||
|
defaultPriority: 'NORMAL',
|
||||||
|
level: 'RENEWAL_GROUP',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
mockPolicyGroupFindMany.mockResolvedValue([
|
||||||
|
{ id: 'group-1', clientId: 'client-1', renewalDate: new Date('2026-08-01') },
|
||||||
|
])
|
||||||
|
|
||||||
|
const result = await runGenerateAndAssign(target, { dryRun: true, actorUserId: 'user-1' })
|
||||||
|
|
||||||
|
expect(mockTaskCreateMany).not.toHaveBeenCalled()
|
||||||
|
expect(mockAuditLogCreate).not.toHaveBeenCalled()
|
||||||
|
expect(result.dryRun).toBe(true)
|
||||||
|
expect(result.totalEstimatedTasks).toBe(1)
|
||||||
|
expect(result.clients).toEqual([{ id: 'client-1', name: 'Client One', estimatedTasks: 1 }])
|
||||||
|
expect(result.advocateName).toBe('Jane Smith')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates and assigns tasks, matching the dry-run count, when dryRun is false', async () => {
|
||||||
|
mockUserFindUnique.mockResolvedValue({ id: 'adv-1', displayName: 'Jane Smith', isActive: true })
|
||||||
|
mockClientsForAdvocateDesignation()
|
||||||
|
mockTaskTemplateFindMany.mockResolvedValue([
|
||||||
|
{
|
||||||
|
id: 'template-1',
|
||||||
|
name: 'Prepare loss summary',
|
||||||
|
description: null,
|
||||||
|
department: 'Commercial Lines',
|
||||||
|
timing: 'PRE_RENEWAL',
|
||||||
|
daysOffset: -1,
|
||||||
|
defaultPriority: 'NORMAL',
|
||||||
|
level: 'RENEWAL_GROUP',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
mockPolicyGroupFindMany.mockResolvedValue([
|
||||||
|
{ id: 'group-1', clientId: 'client-1', renewalDate: new Date('2026-08-01') },
|
||||||
|
])
|
||||||
|
mockTaskCreateMany.mockResolvedValue({ count: 1 })
|
||||||
|
mockTaskFindMany.mockResolvedValue([{ id: 'task-1' }])
|
||||||
|
mockTaskAssignmentCreateMany.mockResolvedValue({ count: 1 })
|
||||||
|
|
||||||
|
const dryRunResult = await runGenerateAndAssign(target, { dryRun: true, actorUserId: 'user-1' })
|
||||||
|
const realResult = await runGenerateAndAssign(target, { dryRun: false, actorUserId: 'user-1' })
|
||||||
|
|
||||||
|
expect(mockTaskCreateMany).toHaveBeenCalledTimes(1)
|
||||||
|
expect(mockAuditLogCreate).toHaveBeenCalledTimes(1)
|
||||||
|
expect(realResult.tasksCreated).toBe(dryRunResult.totalEstimatedTasks)
|
||||||
|
expect(realResult.tasksAssigned).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws 404 when the advocate is not found or inactive', async () => {
|
||||||
|
mockUserFindUnique.mockResolvedValue(null)
|
||||||
|
await expect(runGenerateAndAssign(target, { dryRun: true, actorUserId: 'user-1' })).rejects.toMatchObject({
|
||||||
|
status: 404,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('parseGenerateAndAssignTarget', () => {
|
describe('parseGenerateAndAssignTarget', () => {
|
||||||
it('parses advocate+designation mode', () => {
|
it('parses advocate+designation mode', () => {
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,314 @@ export function parseGenerateAndAssignTarget(body: any): { target: GenerateTarge
|
||||||
}
|
}
|
||||||
throw new GenerateAndAssignError('advocateId and designationId, or clientId, are required', 400)
|
throw new GenerateAndAssignError('advocateId and designationId, or clientId, are required', 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { prisma } from '@/lib/db'
|
||||||
|
import { designationFilter, designationIdsOf } from '@/lib/sync/auto-generate'
|
||||||
|
import { isRelevantDueDate } from '@/lib/sync/task-due-date'
|
||||||
|
|
||||||
|
export interface ClientTaskSummary {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
estimatedTasks: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenerateAndAssignResult {
|
||||||
|
dryRun: boolean
|
||||||
|
clientsFound: number
|
||||||
|
totalEstimatedTasks: number
|
||||||
|
advocateName: string | null
|
||||||
|
clients: ClientTaskSummary[]
|
||||||
|
tasksCreated: number
|
||||||
|
tasksAssigned: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResolvedTarget {
|
||||||
|
advocateId: string
|
||||||
|
advocateName: string | null
|
||||||
|
clientIds: string[]
|
||||||
|
templateDesignationIds: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveTarget(target: GenerateTarget): Promise<ResolvedTarget> {
|
||||||
|
if (target.mode === 'advocate-designation') {
|
||||||
|
const advocate = await prisma.user.findUnique({
|
||||||
|
where: { id: target.advocateId },
|
||||||
|
select: { id: true, displayName: true, isActive: true },
|
||||||
|
})
|
||||||
|
if (!advocate || !advocate.isActive) {
|
||||||
|
throw new GenerateAndAssignError('Advocate not found or inactive', 404)
|
||||||
|
}
|
||||||
|
const clients = await prisma.client.findMany({
|
||||||
|
where: {
|
||||||
|
claimsAdvocateId: target.advocateId,
|
||||||
|
OR: [{ designationId: target.designationId }, { designation2Id: target.designationId }],
|
||||||
|
},
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
advocateId: advocate.id,
|
||||||
|
advocateName: advocate.displayName,
|
||||||
|
clientIds: clients.map((c) => c.id),
|
||||||
|
templateDesignationIds: [target.designationId],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await prisma.client.findUnique({
|
||||||
|
where: { id: target.clientId },
|
||||||
|
select: { id: true, claimsAdvocateId: true, designationId: true, designation2Id: true },
|
||||||
|
})
|
||||||
|
if (!client) {
|
||||||
|
throw new GenerateAndAssignError('Client not found', 404)
|
||||||
|
}
|
||||||
|
if (!client.claimsAdvocateId) {
|
||||||
|
throw new GenerateAndAssignError('Client has no claims advocate assigned', 400)
|
||||||
|
}
|
||||||
|
const advocate = await prisma.user.findUnique({
|
||||||
|
where: { id: client.claimsAdvocateId },
|
||||||
|
select: { id: true, displayName: true, isActive: true },
|
||||||
|
})
|
||||||
|
if (!advocate || !advocate.isActive) {
|
||||||
|
throw new GenerateAndAssignError('Advocate not found or inactive', 404)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
advocateId: advocate.id,
|
||||||
|
advocateName: advocate.displayName,
|
||||||
|
clientIds: [client.id],
|
||||||
|
templateDesignationIds: designationIdsOf(client),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runGenerateAndAssign(
|
||||||
|
target: GenerateTarget,
|
||||||
|
options: { dryRun: boolean; actorUserId: string }
|
||||||
|
): Promise<GenerateAndAssignResult> {
|
||||||
|
const resolved = await resolveTarget(target)
|
||||||
|
|
||||||
|
if (resolved.clientIds.length === 0) {
|
||||||
|
return {
|
||||||
|
dryRun: options.dryRun,
|
||||||
|
clientsFound: 0,
|
||||||
|
totalEstimatedTasks: 0,
|
||||||
|
advocateName: resolved.advocateName,
|
||||||
|
clients: [],
|
||||||
|
tasksCreated: 0,
|
||||||
|
tasksAssigned: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const estimatedByClient = new Map<string, number>()
|
||||||
|
let tasksCreated = 0
|
||||||
|
let tasksAssigned = 0
|
||||||
|
|
||||||
|
const allTemplates = await prisma.taskTemplate.findMany({
|
||||||
|
where: { isActive: true, OR: designationFilter(resolved.templateDesignationIds) },
|
||||||
|
orderBy: [{ department: 'asc' }, { daysOffset: 'asc' }],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (allTemplates.length > 0) {
|
||||||
|
const groupTemplates = allTemplates.filter((t) => t.level === 'BOTH' || t.level === 'RENEWAL_GROUP')
|
||||||
|
const policyTemplates = allTemplates.filter((t) => t.level === 'BOTH' || t.level === 'POLICY')
|
||||||
|
const clientTemplates = allTemplates.filter((t) => t.level === 'CLIENT')
|
||||||
|
|
||||||
|
const groups = await prisma.policyGroup.findMany({
|
||||||
|
where: { clientId: { in: resolved.clientIds }, tasks: { none: { templateId: { not: null } } } },
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const group of groups) {
|
||||||
|
const renewalDate = new Date(group.renewalDate)
|
||||||
|
const tasksToCreate = groupTemplates
|
||||||
|
.map((template) => {
|
||||||
|
const dueDate = new Date(renewalDate)
|
||||||
|
dueDate.setDate(dueDate.getDate() + template.daysOffset)
|
||||||
|
return {
|
||||||
|
title: template.name,
|
||||||
|
description: template.description,
|
||||||
|
department: template.department,
|
||||||
|
timing: template.timing,
|
||||||
|
daysOffset: template.daysOffset,
|
||||||
|
dueDate,
|
||||||
|
status: 'NOT_STARTED' as const,
|
||||||
|
priority: template.defaultPriority,
|
||||||
|
clientId: group.clientId,
|
||||||
|
policyGroupId: group.id,
|
||||||
|
templateId: template.id,
|
||||||
|
createdBy: options.actorUserId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((task) => isRelevantDueDate(task.dueDate))
|
||||||
|
|
||||||
|
if (tasksToCreate.length === 0) continue
|
||||||
|
estimatedByClient.set(group.clientId, (estimatedByClient.get(group.clientId) ?? 0) + tasksToCreate.length)
|
||||||
|
if (options.dryRun) continue
|
||||||
|
|
||||||
|
const created = await prisma.task.createMany({ data: tasksToCreate })
|
||||||
|
tasksCreated += created.count
|
||||||
|
|
||||||
|
if (created.count > 0) {
|
||||||
|
const newTasks = await prisma.task.findMany({
|
||||||
|
where: { policyGroupId: group.id, templateId: { in: groupTemplates.map((t) => t.id) } },
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
if (newTasks.length > 0) {
|
||||||
|
const assigned = await prisma.taskAssignment.createMany({
|
||||||
|
data: newTasks.map((t) => ({ taskId: t.id, userId: resolved.advocateId })),
|
||||||
|
skipDuplicates: true,
|
||||||
|
})
|
||||||
|
tasksAssigned += assigned.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const policies = await prisma.policy.findMany({
|
||||||
|
where: { clientId: { in: resolved.clientIds }, policyGroupId: null, tasks: { none: { templateId: { not: null } } } },
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const policy of policies) {
|
||||||
|
const anchorDate = new Date(policy.expirationDate)
|
||||||
|
anchorDate.setDate(anchorDate.getDate() + 1)
|
||||||
|
const tasksToCreate = policyTemplates
|
||||||
|
.map((template) => {
|
||||||
|
const dueDate = new Date(anchorDate)
|
||||||
|
dueDate.setDate(dueDate.getDate() + template.daysOffset)
|
||||||
|
return {
|
||||||
|
title: template.name,
|
||||||
|
description: template.description,
|
||||||
|
department: template.department,
|
||||||
|
timing: template.timing,
|
||||||
|
daysOffset: template.daysOffset,
|
||||||
|
dueDate,
|
||||||
|
status: 'NOT_STARTED' as const,
|
||||||
|
priority: template.defaultPriority,
|
||||||
|
clientId: policy.clientId,
|
||||||
|
policyId: policy.id,
|
||||||
|
templateId: template.id,
|
||||||
|
createdBy: options.actorUserId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((task) => isRelevantDueDate(task.dueDate))
|
||||||
|
|
||||||
|
if (tasksToCreate.length === 0) continue
|
||||||
|
estimatedByClient.set(policy.clientId, (estimatedByClient.get(policy.clientId) ?? 0) + tasksToCreate.length)
|
||||||
|
if (options.dryRun) continue
|
||||||
|
|
||||||
|
const created = await prisma.task.createMany({ data: tasksToCreate })
|
||||||
|
tasksCreated += created.count
|
||||||
|
|
||||||
|
if (created.count > 0) {
|
||||||
|
const newTasks = await prisma.task.findMany({
|
||||||
|
where: { policyId: policy.id, templateId: { in: policyTemplates.map((t) => t.id) } },
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
if (newTasks.length > 0) {
|
||||||
|
const assigned = await prisma.taskAssignment.createMany({
|
||||||
|
data: newTasks.map((t) => ({ taskId: t.id, userId: resolved.advocateId })),
|
||||||
|
skipDuplicates: true,
|
||||||
|
})
|
||||||
|
tasksAssigned += assigned.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientTemplates.length > 0) {
|
||||||
|
for (const clientId of resolved.clientIds) {
|
||||||
|
const clientRecord = await prisma.client.findUnique({
|
||||||
|
where: { id: clientId },
|
||||||
|
select: {
|
||||||
|
policyGroups: { select: { renewalDate: true } },
|
||||||
|
policies: { where: { policyGroupId: null }, select: { expirationDate: true } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!clientRecord) continue
|
||||||
|
|
||||||
|
const groupDates = clientRecord.policyGroups.map((g) => new Date(g.renewalDate).getTime())
|
||||||
|
const policyDates = clientRecord.policies.map((p) => {
|
||||||
|
const d = new Date(p.expirationDate)
|
||||||
|
d.setDate(d.getDate() + 1)
|
||||||
|
return d.getTime()
|
||||||
|
})
|
||||||
|
const allDates = [...groupDates, ...policyDates]
|
||||||
|
if (allDates.length === 0) continue
|
||||||
|
const anchorDate = new Date(Math.min(...allDates))
|
||||||
|
|
||||||
|
for (const template of clientTemplates) {
|
||||||
|
const existing = await prisma.task.findFirst({
|
||||||
|
where: { clientId, templateId: template.id, policyId: null, policyGroupId: null },
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
if (existing) continue
|
||||||
|
|
||||||
|
const dueDate = new Date(anchorDate)
|
||||||
|
dueDate.setDate(dueDate.getDate() + template.daysOffset)
|
||||||
|
if (!isRelevantDueDate(dueDate)) continue
|
||||||
|
|
||||||
|
estimatedByClient.set(clientId, (estimatedByClient.get(clientId) ?? 0) + 1)
|
||||||
|
if (options.dryRun) continue
|
||||||
|
|
||||||
|
const createdTask = await prisma.task.create({
|
||||||
|
data: {
|
||||||
|
title: template.name,
|
||||||
|
description: template.description,
|
||||||
|
department: template.department,
|
||||||
|
timing: template.timing,
|
||||||
|
daysOffset: template.daysOffset,
|
||||||
|
dueDate,
|
||||||
|
status: 'NOT_STARTED',
|
||||||
|
priority: template.defaultPriority,
|
||||||
|
clientId,
|
||||||
|
templateId: template.id,
|
||||||
|
createdBy: options.actorUserId,
|
||||||
|
},
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
tasksCreated++
|
||||||
|
|
||||||
|
await prisma.taskAssignment.create({ data: { taskId: createdTask.id, userId: resolved.advocateId } })
|
||||||
|
tasksAssigned++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.dryRun) {
|
||||||
|
await prisma.auditLog.create({
|
||||||
|
data: {
|
||||||
|
userId: options.actorUserId,
|
||||||
|
action: 'GENERATE_AND_ASSIGN_TASKS',
|
||||||
|
entityType: 'Task',
|
||||||
|
newValues: {
|
||||||
|
mode: target.mode,
|
||||||
|
...(target.mode === 'advocate-designation'
|
||||||
|
? { advocateId: target.advocateId, designationId: target.designationId }
|
||||||
|
: { clientId: target.clientId }),
|
||||||
|
advocateName: resolved.advocateName,
|
||||||
|
clientsFound: resolved.clientIds.length,
|
||||||
|
tasksCreated,
|
||||||
|
tasksAssigned,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientNames = await prisma.client.findMany({
|
||||||
|
where: { id: { in: resolved.clientIds } },
|
||||||
|
select: { id: true, name: true },
|
||||||
|
})
|
||||||
|
const nameById = new Map(clientNames.map((c) => [c.id, c.name]))
|
||||||
|
|
||||||
|
const clients: ClientTaskSummary[] = resolved.clientIds
|
||||||
|
.map((id) => ({ id, name: nameById.get(id) ?? id, estimatedTasks: estimatedByClient.get(id) ?? 0 }))
|
||||||
|
.filter((c) => c.estimatedTasks > 0)
|
||||||
|
.sort((a, b) => b.estimatedTasks - a.estimatedTasks)
|
||||||
|
|
||||||
|
const totalEstimatedTasks = clients.reduce((sum, c) => sum + c.estimatedTasks, 0)
|
||||||
|
|
||||||
|
return {
|
||||||
|
dryRun: options.dryRun,
|
||||||
|
clientsFound: resolved.clientIds.length,
|
||||||
|
totalEstimatedTasks,
|
||||||
|
advocateName: resolved.advocateName,
|
||||||
|
clients,
|
||||||
|
tasksCreated,
|
||||||
|
tasksAssigned,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue