-- Ticket Workflow Engine Tables -- Refactors the monolithic workflow engine into a flexible, pipeline-like system -- with per-workflow and per-step on/off switches, visual step editing, and -- extensible step executors following the webhook pipeline engine pattern. -- ============================================================================ -- Ticket Workflows — workflow definitions (analogous to webhook_pipelines) -- ============================================================================ CREATE TABLE IF NOT EXISTS ticket_workflows ( id SERIAL PRIMARY KEY, name VARCHAR(200) NOT NULL, description TEXT, is_active BOOLEAN DEFAULT true, trigger_event VARCHAR(50) NOT NULL, -- 'ticket.created', 'ticket.updated' trigger_conditions JSONB NOT NULL DEFAULT '[]', -- array of {field, operator, value} sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- ============================================================================ -- Ticket Workflow Steps — steps within a workflow (analogous to pipeline_steps) -- ============================================================================ CREATE TABLE IF NOT EXISTS ticket_workflow_steps ( id SERIAL PRIMARY KEY, workflow_id INTEGER NOT NULL REFERENCES ticket_workflows(id) ON DELETE CASCADE, step_order INTEGER NOT NULL, step_type VARCHAR(50) NOT NULL, -- 'classify','validate','ai_classify','ai_title','ai_troubleshooting','delay','update_ticket','filter' name VARCHAR(200) NOT NULL, config JSONB NOT NULL DEFAULT '{}', -- step-specific configuration on_failure VARCHAR(20) DEFAULT 'continue', -- 'continue', 'stop', 'skip_to' skip_to_step INTEGER, is_active BOOLEAN DEFAULT true, condition JSONB, -- optional condition to execute this step: {field, operator, value} created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- ============================================================================ -- Ticket Workflow Executions — execution log (replaces workflow_executions) -- ============================================================================ CREATE TABLE IF NOT EXISTS ticket_workflow_executions ( id SERIAL PRIMARY KEY, workflow_id INTEGER NOT NULL REFERENCES ticket_workflows(id) ON DELETE CASCADE, ticket_id BIGINT NOT NULL, ticket_number VARCHAR(50), status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 'pending','running','completed','failed','skipped' classification_method VARCHAR(20), -- 'robotic', 'ai', 'hybrid' branch VARCHAR(20), -- 'service_desk', 'noc', 'soc' context JSONB NOT NULL DEFAULT '{}', -- accumulated data from steps field_changes JSONB, -- final changes applied to ticket started_at TIMESTAMP DEFAULT NOW(), completed_at TIMESTAMP, duration_ms INTEGER, error_message TEXT, created_at TIMESTAMP DEFAULT NOW() ); -- ============================================================================ -- Ticket Workflow Execution Steps — per-step audit trail (replaces workflow_execution_steps) -- ============================================================================ CREATE TABLE IF NOT EXISTS ticket_workflow_execution_steps ( id SERIAL PRIMARY KEY, execution_id INTEGER NOT NULL REFERENCES ticket_workflow_executions(id) ON DELETE CASCADE, step_order INTEGER NOT NULL, step_type VARCHAR(50) NOT NULL, step_name VARCHAR(200), status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 'pending','running','completed','failed','skipped' input_data JSONB, output_data JSONB, started_at TIMESTAMP, completed_at TIMESTAMP, duration_ms INTEGER, error_message TEXT ); -- ============================================================================ -- Indexes -- ============================================================================ CREATE INDEX IF NOT EXISTS idx_ticket_workflows_event ON ticket_workflows(trigger_event, is_active, sort_order); CREATE INDEX IF NOT EXISTS idx_ticket_workflow_steps_workflow ON ticket_workflow_steps(workflow_id, step_order); CREATE INDEX IF NOT EXISTS idx_ticket_workflow_executions_workflow ON ticket_workflow_executions(workflow_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_ticket_workflow_executions_ticket ON ticket_workflow_executions(ticket_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_ticket_workflow_executions_status ON ticket_workflow_executions(status, created_at DESC); CREATE INDEX IF NOT EXISTS idx_ticket_workflow_execution_steps_exec ON ticket_workflow_execution_steps(execution_id, step_order); -- ============================================================================ -- SEED: "Ticket Triage" Workflow (ports current hardcoded workflow-engine.ts logic) -- ============================================================================ INSERT INTO ticket_workflows (name, description, is_active, trigger_event, trigger_conditions, sort_order) VALUES ('Ticket Triage', 'Automatically classifies new tickets using robotic keyword matching, validates the classification, enhances with AI when needed, and writes back to Autotask.', true, 'ticket.created', '[ { "field": "ticket_category", "operator": "in", "value": [2, 3, 159, 161] }, { "field": "creator_resource_id", "operator": "not_in", "value": [30861471] }, { "field": "person_id", "operator": "not_in", "value": [30861575] }, { "field": "company_id", "operator": "not_in", "value": [29861409, 29783545, 29861361, 29702433] } ]', 10); -- Step 1: Classify - Branch Routing INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 1, 'classify', 'Branch Routing', '{ "rule_type": "branch_routing", "result_field": "branch", "default_value": "service_desk" }', true); -- Step 2: Classify - Ticket Type INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 2, 'classify', 'Ticket Type', '{ "rule_type": "ticket_type", "result_field": "ticket_type" }', true); -- Step 3: Classify - Issue Classification INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 3, 'classify', 'Issue Classification', '{ "rule_type": "issue_classification", "result_field": "issue_type", "result_field_2": "sub_issue_type" }', true); -- Step 4: Classify - Priority INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 4, 'classify', 'Priority', '{ "rule_type": "priority", "result_field": "priority" }', true); -- Step 5: Classify - Queue Routing INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 5, 'classify', 'Queue Routing', '{ "rule_type": "queue_routing", "result_field": "queue_id" }', true); -- Step 6: Validate Classification INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, on_failure) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 6, 'validate', 'Validate Classification', '{ "required_fields": [] }', true, 'continue'); -- Step 7: AI Classify (conditional - only if validation failed) INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, condition) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 7, 'ai_classify', 'AI Classification', '{ "template_purpose": "ambiguous_classification", "skip_if_valid": true }', true, '{ "field": "context.validation.is_valid", "operator": "equals", "value": false }'); -- Step 8: AI Title Cleanup (conditional - only if title needs cleanup) INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, condition) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 8, 'ai_title', 'AI Title Cleanup', '{ "template_purpose": "title_cleanup" }', true, '{ "field": "context.classification.ai_reasons", "operator": "contains", "value": "Title" }'); -- Step 9: Delay before Autotask update INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 9, 'delay', 'Delay Before Update', '{ "duration_ms": "{{settings.autotask_update_delay_ms}}" }', true); -- Step 10: Update Ticket in Autotask INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, on_failure) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 10, 'update_ticket', 'Update Autotask Ticket', '{ "use_field_changes": true }', true, 'stop'); -- Step 11: AI Troubleshooting (conditional - only for incidents) INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, condition) VALUES ((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 11, 'ai_troubleshooting', 'Generate Troubleshooting Steps', '{ "template_purpose": "troubleshooting_steps", "create_note": true }', true, '{ "field": "context.field_changes.ticket_type.after", "operator": "equals", "value": 2 }');