/**
* Workflow Tools for NocoBase MCP Server
* Automates creation of CRM workflows
*/
import { NocoBaseClient } from '../client.js';
export interface CreateWorkflowArgs {
title: string;
type: 'collection' | 'schedule';
enabled?: boolean;
description?: string;
config: {
mode?: number;
collection?: string;
appends?: string[];
trigger?: string;
};
nodes?: WorkflowNode[];
}
export interface WorkflowNode {
id: string;
type: string;
title: string;
config: any;
upstream?: string;
downstream?: string;
}
/**
* Create a workflow
*/
export async function createWorkflow(
client: NocoBaseClient,
args: CreateWorkflowArgs
): Promise<any> {
console.error(`[Workflow] Creating workflow: ${args.title}`);
const workflowData = {
title: args.title,
type: args.type,
enabled: args.enabled !== false,
description: args.description || '',
config: args.config,
};
// Create workflow
const response = await client.post('/workflows:create', workflowData);
const workflow = response.data.data;
console.error(`[Workflow] Created workflow ID: ${workflow.id}`);
// Add nodes if provided
if (args.nodes && args.nodes.length > 0) {
for (const node of args.nodes) {
await client.post(`/workflows/${workflow.id}/nodes:create`, {
...node,
workflowId: workflow.id
});
console.error(`[Workflow] Added node: ${node.title}`);
}
}
return {
success: true,
workflow,
message: `Created workflow: ${args.title}`
};
}
/**
* Create all CRM workflows
*/
export async function createCRMWorkflows(client: NocoBaseClient): Promise<any> {
const workflows = [
createLeadAutoAssignmentWorkflow(),
createLeadConversionWorkflow(),
createBoothHoldWorkflow(),
createBoothReleaseWorkflow(),
createSOWCreationWorkflow(),
];
const results = [];
const errors = [];
for (const workflowConfig of workflows) {
try {
const result = await createWorkflow(client, workflowConfig);
results.push(result);
} catch (error: any) {
console.error(`[Workflow] Error creating ${workflowConfig.title}:`, error.message);
errors.push({
workflow: workflowConfig.title,
error: error.message
});
}
}
return {
success: errors.length === 0,
created: results.length,
failed: errors.length,
results,
errors
};
}
/**
* Workflow 1: Lead Auto-Assignment
*/
function createLeadAutoAssignmentWorkflow(): CreateWorkflowArgs {
return {
title: 'Lead Auto-Assignment',
type: 'collection',
enabled: true,
description: 'Automatically assign new leads to sales reps using round-robin',
config: {
mode: 1, // After record added
collection: 'leads',
},
nodes: [
{
id: 'check_owner',
type: 'condition',
title: 'Check if owner is empty',
config: {
calculation: {
calculator: 'boolean',
operands: [
{ type: 'context', options: { path: 'data.owner_id' } },
{ type: 'constant', value: null }
],
operator: '=='
}
},
upstream: undefined,
downstream: 'assign_owner'
},
{
id: 'assign_owner',
type: 'update',
title: 'Assign to next available rep',
config: {
collection: 'leads',
params: {
filter: {
id: '{{$context.data.id}}'
},
values: {
owner_id: '{{$fn.getNextSalesRep()}}' // Custom function needed
}
}
},
upstream: 'check_owner',
downstream: 'create_task'
},
{
id: 'create_task',
type: 'create',
title: 'Create follow-up task',
config: {
collection: 'activities',
params: {
values: {
activity_type: 'task',
subject: 'Follow up on new lead: {{$context.data.company_name}}',
owner_id: '{{$context.data.owner_id}}',
related_to_type: 'lead',
related_to_id: '{{$context.data.id}}',
due_date: '{{$fn.addDays($now, 1)}}',
priority: 'high',
status: 'planned'
}
}
},
upstream: 'assign_owner',
downstream: undefined
}
]
};
}
/**
* Workflow 2: Lead Conversion
*/
function createLeadConversionWorkflow(): CreateWorkflowArgs {
return {
title: 'Lead Conversion',
type: 'collection',
enabled: true,
description: 'Convert qualified lead to account + contact + opportunity',
config: {
mode: 2, // After record updated
collection: 'leads',
},
nodes: [
{
id: 'check_status',
type: 'condition',
title: 'Check if status = qualified',
config: {
calculation: {
calculator: 'boolean',
operands: [
{ type: 'context', options: { path: 'data.status' } },
{ type: 'constant', value: 'qualified' }
],
operator: '=='
}
},
upstream: undefined,
downstream: 'create_account'
},
{
id: 'create_account',
type: 'create',
title: 'Create Account',
config: {
collection: 'accounts',
params: {
values: {
name: '{{$context.data.company_name}}',
account_type: 'prospect',
industry: '{{$context.data.industry}}',
phone: '{{$context.data.phone}}',
email: '{{$context.data.email}}',
tenant_id: '{{$context.data.tenant_id}}'
}
}
},
upstream: 'check_status',
downstream: 'create_contact'
},
{
id: 'create_contact',
type: 'create',
title: 'Create Contact',
config: {
collection: 'contacts',
params: {
values: {
account_id: '{{$jobsMapByNodeId.create_account.id}}',
first_name: '{{$fn.split($context.data.contact_name, " ")[0]}}',
last_name: '{{$fn.split($context.data.contact_name, " ")[1]}}',
email: '{{$context.data.email}}',
phone: '{{$context.data.phone}}',
job_title: '{{$context.data.job_title}}',
is_primary: true,
tenant_id: '{{$context.data.tenant_id}}'
}
}
},
upstream: 'create_account',
downstream: 'create_opportunity'
},
{
id: 'create_opportunity',
type: 'create',
title: 'Create Opportunity',
config: {
collection: 'opportunities',
params: {
values: {
account_id: '{{$jobsMapByNodeId.create_account.id}}',
primary_contact_id: '{{$jobsMapByNodeId.create_contact.id}}',
lead_id: '{{$context.data.id}}',
owner_id: '{{$context.data.owner_id}}',
opportunity_name: '{{$context.data.company_name}} - {{$context.data.business_line}} Deal',
pipeline_type: '{{$context.data.business_line}}',
stage: 'new_lead',
probability: 10,
amount: 0,
tenant_id: '{{$context.data.tenant_id}}'
}
}
},
upstream: 'create_contact',
downstream: 'update_lead'
},
{
id: 'update_lead',
type: 'update',
title: 'Mark lead as converted',
config: {
collection: 'leads',
params: {
filter: {
id: '{{$context.data.id}}'
},
values: {
is_converted: true,
converted_date: '{{$now}}',
status: 'converted'
}
}
},
upstream: 'create_opportunity',
downstream: undefined
}
]
};
}
/**
* Workflow 3: Booth Hold (Exhibition)
*/
function createBoothHoldWorkflow(): CreateWorkflowArgs {
return {
title: 'Booth Hold - Exhibition',
type: 'collection',
enabled: true,
description: 'Hold booth when opportunity moves to Negotiation stage',
config: {
mode: 2, // After record updated
collection: 'opportunities',
},
nodes: [
{
id: 'check_stage',
type: 'condition',
title: 'Check if stage = Negotiation',
config: {
calculation: {
calculator: 'boolean',
operands: [
{ type: 'context', options: { path: 'data.stage' } },
{ type: 'constant', value: 'negotiation' }
],
operator: '=='
}
},
upstream: undefined,
downstream: 'check_pipeline'
},
{
id: 'check_pipeline',
type: 'condition',
title: 'Check if pipeline = exhibition',
config: {
calculation: {
calculator: 'boolean',
operands: [
{ type: 'context', options: { path: 'data.pipeline_type' } },
{ type: 'constant', value: 'exhibition' }
],
operator: '=='
}
},
upstream: 'check_stage',
downstream: 'query_booths'
},
{
id: 'query_booths',
type: 'query',
title: 'Get booth items',
config: {
collection: 'opportunity_items',
params: {
filter: {
opportunity_id: '{{$context.data.id}}',
item_type: 'booth'
}
}
},
upstream: 'check_pipeline',
downstream: 'update_booths'
},
{
id: 'update_booths',
type: 'update',
title: 'Hold booths',
config: {
collection: 'booth_inventory',
multiple: true,
params: {
filter: {
id: '{{$jobsMapByNodeId.query_booths.booth_inventory_id}}'
},
values: {
status: 'held',
held_by_opportunity_id: '{{$context.data.id}}',
held_until: '{{$fn.addDays($now, 7)}}'
}
}
},
upstream: 'query_booths',
downstream: undefined
}
]
};
}
/**
* Workflow 4: Booth Release (Lost Deal)
*/
function createBoothReleaseWorkflow(): CreateWorkflowArgs {
return {
title: 'Booth Release - Lost Deal',
type: 'collection',
enabled: true,
description: 'Release held booths when opportunity is lost',
config: {
mode: 2, // After record updated
collection: 'opportunities',
},
nodes: [
{
id: 'check_stage',
type: 'condition',
title: 'Check if stage = Closed Lost',
config: {
calculation: {
calculator: 'boolean',
operands: [
{ type: 'context', options: { path: 'data.stage' } },
{ type: 'constant', value: 'closed_lost' }
],
operator: '=='
}
},
upstream: undefined,
downstream: 'release_booths'
},
{
id: 'release_booths',
type: 'update',
title: 'Release all held booths',
config: {
collection: 'booth_inventory',
multiple: true,
params: {
filter: {
held_by_opportunity_id: '{{$context.data.id}}'
},
values: {
status: 'available',
held_by_opportunity_id: null,
held_until: undefined
}
}
},
upstream: 'check_stage',
downstream: undefined
}
]
};
}
/**
* Workflow 5: SOW Creation (Agency)
*/
function createSOWCreationWorkflow(): CreateWorkflowArgs {
return {
title: 'SOW Creation - Agency',
type: 'collection',
enabled: true,
description: 'Auto-create Statement of Work when agency deal is won',
config: {
mode: 2, // After record updated
collection: 'opportunities',
},
nodes: [
{
id: 'check_stage',
type: 'condition',
title: 'Check if stage = Won Deal',
config: {
calculation: {
calculator: 'boolean',
operands: [
{ type: 'context', options: { path: 'data.stage' } },
{ type: 'constant', value: 'won_deal' }
],
operator: '=='
}
},
upstream: undefined,
downstream: 'check_pipeline'
},
{
id: 'check_pipeline',
type: 'condition',
title: 'Check if pipeline = agency',
config: {
calculation: {
calculator: 'boolean',
operands: [
{ type: 'context', options: { path: 'data.pipeline_type' } },
{ type: 'constant', value: 'agency' }
],
operator: '=='
}
},
upstream: 'check_stage',
downstream: 'create_sow'
},
{
id: 'create_sow',
type: 'create',
title: 'Create SOW',
config: {
collection: 'sows',
params: {
values: {
tenant_id: '{{$context.data.tenant_id}}',
opportunity_id: '{{$context.data.id}}',
account_id: '{{$context.data.account_id}}',
sow_number: '{{$fn.generateSOWNumber()}}',
sow_name: '{{$context.data.opportunity_name}}',
total_value: '{{$context.data.amount}}',
currency: '{{$context.data.currency}}',
status: 'draft',
start_date: '{{$context.data.actual_close_date}}'
}
}
},
upstream: 'check_pipeline',
downstream: 'update_account'
},
{
id: 'update_account',
type: 'update',
title: 'Update account to customer',
config: {
collection: 'accounts',
params: {
filter: {
id: '{{$context.data.account_id}}'
},
values: {
account_type: 'customer'
}
}
},
upstream: 'create_sow',
downstream: undefined
}
]
};
}