Workflow Automation Guide

Workflow Automation Guide

Design and implement sophisticated automated business processes using BigLedger’s comprehensive automation tools. Perfect for DevOps engineers, process automation experts, and workflow designers.

ℹ️
Event-Driven Automation: BigLedger’s automation framework is built around events, webhooks, and intelligent workflow orchestration that can handle complex business logic and approval processes.

Automation Overview

BigLedger provides multiple automation mechanisms:

  • Webhook-Triggered Workflows: React to business events in real-time
  • Scheduled Operations: Time-based automation for reports, backups, and maintenance
  • Business Rule Engine: Automated decision making and approval workflows
  • Batch Processing: Handle large-scale data operations efficiently
  • Integration Automation: Sync data between BigLedger and external systems

Event-Driven Workflows

Setting Up Webhook-Based Automation

// workflow-manager.js
const express = require('express');
const { WorkflowEngine } = require('@bigledger/workflow-sdk');
const { BigLedgerClient } = require('@bigledger/sdk');

class AutomationManager {
  constructor() {
    this.workflowEngine = new WorkflowEngine({
      apiKey: process.env.BIGLEDGER_API_KEY,
      companyId: process.env.BIGLEDGER_COMPANY_ID
    });
    
    this.bigledger = new BigLedgerClient({
      apiKey: process.env.BIGLEDGER_API_KEY,
      companyId: process.env.BIGLEDGER_COMPANY_ID
    });
  }

  async setupWebhookHandlers() {
    // Invoice automation workflows
    this.workflowEngine.on('invoice.created', this.handleInvoiceCreated.bind(this));
    this.workflowEngine.on('invoice.overdue', this.handleOverdueInvoice.bind(this));
    this.workflowEngine.on('payment.received', this.handlePaymentReceived.bind(this));
    
    // Inventory automation workflows
    this.workflowEngine.on('inventory.low_stock', this.handleLowStock.bind(this));
    this.workflowEngine.on('inventory.out_of_stock', this.handleOutOfStock.bind(this));
    
    // Purchase automation workflows
    this.workflowEngine.on('purchase_order.approved', this.handlePurchaseOrderApproved.bind(this));
    this.workflowEngine.on('bill.received', this.handleBillReceived.bind(this));
  }

  async handleInvoiceCreated(event) {
    const invoice = event.data.object;
    
    // Start automated invoice processing workflow
    await this.workflowEngine.startWorkflow('invoice-processing', {
      invoiceId: invoice.id,
      customerId: invoice.customerId,
      amount: invoice.total,
      priority: invoice.total > 10000 ? 'high' : 'normal'
    });
  }

  async handleOverdueInvoice(event) {
    const invoice = event.data.object;
    
    // Start automated dunning process
    await this.workflowEngine.startWorkflow('dunning-process', {
      invoiceId: invoice.id,
      customerId: invoice.customerId,
      overdueAmount: invoice.amountDue,
      overdueDays: invoice.overdueDays
    });
  }
}

Complex Workflow Example: Invoice Processing

// workflows/invoice-processing.js
class InvoiceProcessingWorkflow {
  constructor(workflowContext) {
    this.context = workflowContext;
    this.bigledger = workflowContext.bigledger;
  }

  async execute() {
    const { invoiceId, customerId, amount, priority } = this.context.input;
    
    try {
      // Step 1: Validate invoice data
      await this.validateInvoiceData(invoiceId);
      
      // Step 2: Check customer credit limit
      const creditCheck = await this.checkCustomerCredit(customerId, amount);
      
      // Step 3: Apply business rules
      const businessRules = await this.applyBusinessRules(invoiceId, creditCheck);
      
      // Step 4: Generate e-invoice if required
      if (businessRules.requiresEInvoice) {
        await this.generateEInvoice(invoiceId);
      }
      
      // Step 5: Send invoice to customer
      await this.sendInvoiceToCustomer(invoiceId, businessRules.deliveryMethod);
      
      // Step 6: Schedule follow-up actions
      await this.scheduleFollowUpActions(invoiceId, businessRules);
      
      // Step 7: Update CRM and analytics
      await this.updateCRMAndAnalytics(customerId, invoiceId);
      
      return { status: 'completed', invoiceId, actions: businessRules.actions };
      
    } catch (error) {
      // Handle errors and trigger remediation workflows
      await this.handleWorkflowError(error, invoiceId);
      throw error;
    }
  }

  async validateInvoiceData(invoiceId) {
    const invoice = await this.bigledger.invoices.get(invoiceId);
    
    const validations = [
      { field: 'items', check: (inv) => inv.items && inv.items.length > 0 },
      { field: 'customer', check: (inv) => inv.customerId },
      { field: 'total', check: (inv) => inv.total > 0 },
      { field: 'dueDate', check: (inv) => inv.dueDate }
    ];

    const errors = validations
      .filter(v => !v.check(invoice))
      .map(v => `Missing or invalid: ${v.field}`);

    if (errors.length > 0) {
      throw new ValidationError(`Invoice validation failed: ${errors.join(', ')}`);
    }
  }

  async checkCustomerCredit(customerId, amount) {
    const customer = await this.bigledger.customers.get(customerId);
    const outstandingBalance = await this.bigledger.customers.getOutstandingBalance(customerId);
    
    return {
      creditLimit: customer.creditLimit,
      currentBalance: outstandingBalance.total,
      newBalance: outstandingBalance.total + amount,
      withinLimit: (outstandingBalance.total + amount) <= customer.creditLimit,
      riskLevel: this.calculateRiskLevel(customer, outstandingBalance.total + amount)
    };
  }

  async applyBusinessRules(invoiceId, creditCheck) {
    const invoice = await this.bigledger.invoices.get(invoiceId);
    const customer = await this.bigledger.customers.get(invoice.customerId);
    
    const rules = {
      requiresEInvoice: customer.country === 'MY' && invoice.total >= 100,
      requiresApproval: creditCheck.riskLevel === 'high' || invoice.total > 50000,
      deliveryMethod: customer.preferences?.invoiceDelivery || 'email',
      paymentTerms: this.calculatePaymentTerms(customer, creditCheck),
      actions: []
    };

    // Apply conditional logic
    if (!creditCheck.withinLimit) {
      rules.requiresApproval = true;
      rules.actions.push({
        type: 'credit_limit_exceeded_notification',
        recipients: ['credit_manager@company.com'],
        data: { customerId: customer.id, exceedAmount: creditCheck.newBalance - creditCheck.creditLimit }
      });
    }

    if (invoice.total > 25000) {
      rules.actions.push({
        type: 'high_value_invoice_notification',
        recipients: ['finance_director@company.com'],
        data: { invoiceId, amount: invoice.total }
      });
    }

    return rules;
  }

  async generateEInvoice(invoiceId) {
    try {
      const einvoice = await this.bigledger.einvoice.create({
        invoiceId,
        format: 'PEPPOL_UBL',
        autoSubmit: true
      });

      // Monitor e-invoice status
      await this.context.scheduleTask('monitor-einvoice-status', {
        einvoiceId: einvoice.id,
        maxAttempts: 10,
        intervalMinutes: 5
      });

      return einvoice;
    } catch (error) {
      // Handle e-invoice generation errors
      await this.context.createTask('einvoice-manual-review', {
        invoiceId,
        error: error.message,
        assignee: 'compliance_team'
      });
      throw error;
    }
  }

  async sendInvoiceToCustomer(invoiceId, deliveryMethod) {
    const methods = {
      email: () => this.sendInvoiceByEmail(invoiceId),
      portal: () => this.publishToCustomerPortal(invoiceId),
      api: () => this.sendViaCustomerAPI(invoiceId),
      postal: () => this.schedulePostalDelivery(invoiceId)
    };

    if (methods[deliveryMethod]) {
      await methods[deliveryMethod]();
    } else {
      // Default to email
      await this.sendInvoiceByEmail(invoiceId);
    }
  }

  async scheduleFollowUpActions(invoiceId, businessRules) {
    const invoice = await this.bigledger.invoices.get(invoiceId);
    
    // Schedule payment reminder
    const reminderDate = new Date(invoice.dueDate);
    reminderDate.setDate(reminderDate.getDate() - 3); // 3 days before due
    
    await this.context.scheduleWorkflow('payment-reminder', {
      invoiceId,
      customerId: invoice.customerId
    }, reminderDate);

    // Schedule overdue follow-up
    const overdueDate = new Date(invoice.dueDate);
    overdueDate.setDate(overdueDate.getDate() + 1); // 1 day after due
    
    await this.context.scheduleWorkflow('overdue-follow-up', {
      invoiceId,
      customerId: invoice.customerId
    }, overdueDate);
  }
}

Business Rule Engine

Defining Business Rules

// business-rules/invoice-rules.js
const { RuleEngine } = require('@bigledger/workflow-sdk');

class InvoiceBusinessRules extends RuleEngine {
  
  constructor() {
    super();
    this.defineRules();
  }

  defineRules() {
    // Rule: High-value invoice approval
    this.addRule('high-value-approval', {
      when: (facts) => facts.invoice.total > 50000,
      then: (facts, actions) => {
        actions.requireApproval({
          approvers: ['finance_director', 'ceo'],
          reason: 'High value invoice requires executive approval'
        });
      }
    });

    // Rule: New customer credit check
    this.addRule('new-customer-credit-check', {
      when: (facts) => facts.customer.isNew && facts.invoice.total > 5000,
      then: (facts, actions) => {
        actions.requireCreditCheck({
          customerId: facts.customer.id,
          requestedAmount: facts.invoice.total
        });
      }
    });

    // Rule: Multi-currency invoice validation
    this.addRule('multi-currency-validation', {
      when: (facts) => facts.invoice.currency !== facts.company.baseCurrency,
      then: (facts, actions) => {
        actions.validateExchangeRate({
          fromCurrency: facts.invoice.currency,
          toCurrency: facts.company.baseCurrency,
          amount: facts.invoice.total,
          date: facts.invoice.invoiceDate
        });
      }
    });

    // Rule: Automatic discount application
    this.addRule('volume-discount', {
      when: (facts) => facts.customer.tier === 'premium' && facts.invoice.total > 10000,
      then: (facts, actions) => {
        actions.applyDiscount({
          type: 'percentage',
          value: 5,
          reason: 'Premium customer volume discount'
        });
      }
    });

    // Rule: Tax validation
    this.addRule('tax-validation', {
      when: (facts) => facts.invoice.items.some(item => !item.taxCode),
      then: (facts, actions) => {
        actions.flagForTaxReview({
          invoiceId: facts.invoice.id,
          reason: 'Missing tax codes on line items'
        });
      }
    });
  }

  async executeRules(invoiceData) {
    const facts = await this.prepareFacts(invoiceData);
    const results = await this.run(facts);
    
    return {
      rules_applied: results.events.map(e => e.type),
      actions_required: results.events.map(e => e.params),
      approval_required: results.events.some(e => e.type === 'requireApproval'),
      warnings: results.events.filter(e => e.type.includes('warning')),
      errors: results.events.filter(e => e.type.includes('error'))
    };
  }

  async prepareFacts(invoiceData) {
    const invoice = await this.bigledger.invoices.get(invoiceData.invoiceId);
    const customer = await this.bigledger.customers.get(invoice.customerId);
    const company = await this.bigledger.company.get();
    
    return {
      invoice: {
        ...invoice,
        isNew: !invoice.invoiceNumber,
        age: Math.floor((Date.now() - new Date(invoice.createdAt)) / (1000 * 60 * 60 * 24))
      },
      customer: {
        ...customer,
        isNew: customer.createdAt > Date.now() - (30 * 24 * 60 * 60 * 1000), // 30 days
        outstandingBalance: await this.getCustomerBalance(customer.id)
      },
      company
    };
  }
}

Scheduled Automation

Automated Report Generation

// schedulers/report-scheduler.js
const cron = require('node-cron');
const { ReportGenerator } = require('@bigledger/reporting-sdk');

class AutomatedReportScheduler {
  
  constructor() {
    this.reportGenerator = new ReportGenerator({
      apiKey: process.env.BIGLEDGER_API_KEY,
      companyId: process.env.BIGLEDGER_COMPANY_ID
    });
  }

  start() {
    // Daily reports
    cron.schedule('0 9 * * 1-5', () => {
      this.generateDailyReports();
    }, { timezone: 'Asia/Kuala_Lumpur' });

    // Weekly reports  
    cron.schedule('0 10 * * 1', () => {
      this.generateWeeklyReports();
    });

    // Monthly reports
    cron.schedule('0 9 1 * *', () => {
      this.generateMonthlyReports();
    });

    // End-of-quarter reports
    cron.schedule('0 8 1 1,4,7,10 *', () => {
      this.generateQuarterlyReports();
    });
  }

  async generateDailyReports() {
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    
    const reports = [
      {
        name: 'Daily Sales Summary',
        type: 'sales_summary',
        dateRange: { from: yesterday, to: yesterday },
        recipients: ['sales@company.com', 'management@company.com']
      },
      {
        name: 'Cash Flow Report',
        type: 'cash_flow',
        dateRange: { from: yesterday, to: yesterday },
        recipients: ['finance@company.com']
      },
      {
        name: 'Outstanding Invoices',
        type: 'aged_receivables',
        asOfDate: yesterday,
        recipients: ['accounts@company.com']
      }
    ];

    for (const reportConfig of reports) {
      try {
        await this.generateAndDistributeReport(reportConfig);
      } catch (error) {
        console.error(`Failed to generate ${reportConfig.name}:`, error);
        await this.notifyReportFailure(reportConfig, error);
      }
    }
  }

  async generateWeeklyReports() {
    const lastWeek = this.getLastWeekRange();
    
    const reports = [
      {
        name: 'Weekly Financial Summary',
        type: 'financial_summary',
        dateRange: lastWeek,
        recipients: ['ceo@company.com', 'cfo@company.com'],
        format: 'pdf'
      },
      {
        name: 'Inventory Movement Report',
        type: 'inventory_movement',
        dateRange: lastWeek,
        recipients: ['warehouse@company.com', 'purchasing@company.com']
      },
      {
        name: 'Customer Payment Analysis',
        type: 'payment_analysis',
        dateRange: lastWeek,
        recipients: ['credit_control@company.com']
      }
    ];

    await this.processReportsInBatch(reports);
  }

  async generateAndDistributeReport(config) {
    // Generate the report
    const report = await this.reportGenerator.generate({
      type: config.type,
      parameters: {
        dateRange: config.dateRange,
        asOfDate: config.asOfDate,
        format: config.format || 'pdf',
        includeCharts: true,
        includeComparisons: true
      }
    });

    // Add executive summary for management reports
    if (config.recipients.some(email => email.includes('ceo') || email.includes('management'))) {
      report.executiveSummary = await this.generateExecutiveSummary(report);
    }

    // Distribute via email
    await this.distributeReport(report, config.recipients, config.name);

    // Store in document management system
    await this.archiveReport(report, config);

    // Log the generation
    console.log(`✅ Generated and distributed: ${config.name}`);
  }
}

Automated Maintenance Tasks

// maintenance/automated-maintenance.js
class AutomatedMaintenanceTasks {
  
  constructor() {
    this.bigledger = new BigLedgerClient({
      apiKey: process.env.BIGLEDGER_API_KEY,
      companyId: process.env.BIGLEDGER_COMPANY_ID
    });
  }

  startMaintenanceTasks() {
    // Daily cleanup tasks
    cron.schedule('0 2 * * *', () => {
      this.runDailyMaintenance();
    });

    // Weekly optimization tasks
    cron.schedule('0 3 * * 0', () => {
      this.runWeeklyMaintenance();
    });

    // Monthly archival tasks  
    cron.schedule('0 4 1 * *', () => {
      this.runMonthlyMaintenance();
    });
  }

  async runDailyMaintenance() {
    const tasks = [
      { name: 'Clean temporary files', fn: () => this.cleanTemporaryFiles() },
      { name: 'Update exchange rates', fn: () => this.updateExchangeRates() },
      { name: 'Process pending e-invoices', fn: () => this.processPendingEInvoices() },
      { name: 'Check system health', fn: () => this.performHealthCheck() },
      { name: 'Backup critical data', fn: () => this.backupCriticalData() }
    ];

    await this.executeMaintenance('Daily', tasks);
  }

  async runWeeklyMaintenance() {
    const tasks = [
      { name: 'Optimize database indexes', fn: () => this.optimizeDatabase() },
      { name: 'Archive old audit logs', fn: () => this.archiveAuditLogs() },
      { name: 'Update currency rates history', fn: () => this.updateCurrencyHistory() },
      { name: 'Clean up expired sessions', fn: () => this.cleanExpiredSessions() },
      { name: 'Generate performance reports', fn: () => this.generatePerformanceReports() }
    ];

    await this.executeMaintenance('Weekly', tasks);
  }

  async runMonthlyMaintenance() {
    const tasks = [
      { name: 'Archive completed transactions', fn: () => this.archiveTransactions() },
      { name: 'Update customer risk scores', fn: () => this.updateRiskScores() },
      { name: 'Cleanup old document versions', fn: () => this.cleanupDocumentVersions() },
      { name: 'Generate monthly compliance report', fn: () => this.generateComplianceReport() }
    ];

    await this.executeMaintenance('Monthly', tasks);
  }

  async executeMaintenance(type, tasks) {
    const startTime = Date.now();
    const results = [];

    console.log(`🔧 Starting ${type.toLowerCase()} maintenance tasks...`);

    for (const task of tasks) {
      const taskStart = Date.now();
      try {
        await task.fn();
        const duration = Date.now() - taskStart;
        results.push({ name: task.name, status: 'success', duration });
        console.log(`✅ ${task.name} completed in ${duration}ms`);
      } catch (error) {
        const duration = Date.now() - taskStart;
        results.push({ name: task.name, status: 'failed', duration, error: error.message });
        console.error(`❌ ${task.name} failed:`, error.message);
      }
    }

    const totalDuration = Date.now() - startTime;
    const summary = {
      type,
      totalDuration,
      tasksCompleted: results.filter(r => r.status === 'success').length,
      tasksFailed: results.filter(r => r.status === 'failed').length,
      results
    };

    // Send maintenance report
    await this.sendMaintenanceReport(summary);
    
    console.log(`🏁 ${type} maintenance completed in ${totalDuration}ms`);
  }
}

Integration Automation

Third-Party System Synchronization

// integrations/sync-manager.js
class IntegrationSyncManager {
  
  constructor() {
    this.bigledger = new BigLedgerClient({
      apiKey: process.env.BIGLEDGER_API_KEY,
      companyId: process.env.BIGLEDGER_COMPANY_ID
    });
    
    this.integrations = {
      shopify: new ShopifyIntegration(),
      stripe: new StripeIntegration(),
      quickbooks: new QuickBooksIntegration(),
      warehouse: new WarehouseIntegration()
    };
  }

  startSyncProcesses() {
    // Real-time sync via webhooks
    this.setupWebhookSync();
    
    // Scheduled batch sync
    this.setupBatchSync();
    
    // Error recovery sync
    this.setupErrorRecovery();
  }

  setupWebhookSync() {
    // Shopify order sync
    this.bigledger.webhooks.on('sales_order.created', async (event) => {
      await this.syncOrderToShopify(event.data.object);
    });

    // Inventory sync
    this.bigledger.webhooks.on('inventory.adjustment', async (event) => {
      await this.syncInventoryToAllPlatforms(event.data.object);
    });

    // Payment sync
    this.bigledger.webhooks.on('payment.received', async (event) => {
      await this.syncPaymentToAccounting(event.data.object);
    });
  }

  setupBatchSync() {
    // Hourly customer sync
    cron.schedule('0 * * * *', () => {
      this.syncCustomersFromAllPlatforms();
    });

    // Daily product sync
    cron.schedule('0 6 * * *', () => {
      this.syncProductsToAllPlatforms();
    });

    // Daily financial sync
    cron.schedule('0 23 * * *', () => {
      this.syncFinancialDataToAccounting();
    });
  }

  async syncOrderToShopify(order) {
    try {
      const shopifyOrder = await this.transformOrderForShopify(order);
      
      // Create or update order in Shopify
      const result = await this.integrations.shopify.orders.upsert(shopifyOrder);
      
      // Update BigLedger with Shopify order ID
      await this.bigledger.salesOrders.update(order.id, {
        externalReferences: {
          shopify: result.id
        }
      });

      // Sync inventory levels
      await this.syncInventoryToShopify(order.items);
      
      console.log(`✅ Order ${order.orderNumber} synced to Shopify`);
      
    } catch (error) {
      console.error(`❌ Failed to sync order ${order.id} to Shopify:`, error);
      await this.queueRetrySync('shopify', 'order', order.id, error);
    }
  }

  async syncInventoryToAllPlatforms(inventoryItem) {
    const syncTasks = Object.entries(this.integrations).map(async ([platform, integration]) => {
      try {
        if (integration.inventory && integration.inventory.updateStock) {
          await integration.inventory.updateStock({
            sku: inventoryItem.sku,
            quantity: inventoryItem.currentStock,
            location: inventoryItem.location
          });
          
          console.log(`✅ Inventory synced to ${platform}: ${inventoryItem.sku}`);
        }
      } catch (error) {
        console.error(`❌ Failed to sync inventory to ${platform}:`, error);
        await this.queueRetrySync(platform, 'inventory', inventoryItem.id, error);
      }
    });

    await Promise.allSettled(syncTasks);
  }

  async setupErrorRecovery() {
    // Process failed sync queue every 15 minutes
    cron.schedule('*/15 * * * *', () => {
      this.processFailedSyncQueue();
    });
  }

  async processFailedSyncQueue() {
    const failedSyncs = await this.getFailedSyncs();
    
    for (const sync of failedSyncs) {
      if (sync.retryCount < 5) {
        try {
          await this.retrySyncOperation(sync);
          await this.markSyncAsSuccessful(sync.id);
          console.log(`✅ Retry successful for ${sync.platform} ${sync.type}`);
        } catch (error) {
          await this.incrementRetryCount(sync.id);
          console.error(`❌ Retry failed for ${sync.platform} ${sync.type}:`, error);
        }
      } else {
        // Mark as permanently failed and notify administrators
        await this.markSyncAsFailedPermanently(sync.id);
        await this.notifyAdministrators(sync);
      }
    }
  }
}

Approval Workflows

Multi-Stage Approval Process

// workflows/approval-workflow.js
class ApprovalWorkflow {
  
  constructor(workflowContext) {
    this.context = workflowContext;
    this.bigledger = workflowContext.bigledger;
  }

  async execute() {
    const { entityType, entityId, approvalType, requestedBy } = this.context.input;
    
    try {
      // Get approval configuration
      const approvalConfig = await this.getApprovalConfig(entityType, approvalType);
      
      // Create approval request
      const approvalRequest = await this.createApprovalRequest({
        entityType,
        entityId,
        approvalType,
        requestedBy,
        approvers: approvalConfig.approvers,
        rules: approvalConfig.rules
      });

      // Start approval process
      return await this.processApproval(approvalRequest, approvalConfig);
      
    } catch (error) {
      await this.handleApprovalError(error);
      throw error;
    }
  }

  async getApprovalConfig(entityType, approvalType) {
    const configs = {
      'purchase_order': {
        'standard': {
          approvers: [
            { role: 'department_manager', required: true, order: 1 },
            { role: 'finance_manager', required: true, order: 2 }
          ],
          rules: {
            maxAmount: 10000,
            timeoutHours: 48
          }
        },
        'high_value': {
          approvers: [
            { role: 'department_manager', required: true, order: 1 },
            { role: 'finance_manager', required: true, order: 2 },
            { role: 'cfo', required: true, order: 3 },
            { role: 'ceo', required: false, order: 4 }
          ],
          rules: {
            minAmount: 10000,
            timeoutHours: 72
          }
        }
      },
      'expense_report': {
        'standard': {
          approvers: [
            { role: 'line_manager', required: true, order: 1 }
          ],
          rules: {
            maxAmount: 1000,
            timeoutHours: 24
          }
        },
        'high_value': {
          approvers: [
            { role: 'line_manager', required: true, order: 1 },
            { role: 'finance_manager', required: true, order: 2 }
          ],
          rules: {
            minAmount: 1000,
            timeoutHours: 48
          }
        }
      }
    };

    return configs[entityType]?.[approvalType] || configs[entityType]?.['standard'];
  }

  async processApproval(approvalRequest, config) {
    const approvers = config.approvers.sort((a, b) => a.order - b.order);
    
    for (const approver of approvers) {
      if (approver.required) {
        // Send approval notification
        await this.sendApprovalNotification(approvalRequest, approver);
        
        // Wait for approval with timeout
        const approval = await this.waitForApproval(
          approvalRequest.id, 
          approver.role, 
          config.rules.timeoutHours
        );

        if (approval.status === 'approved') {
          await this.recordApproval(approvalRequest.id, approver.role, approval);
        } else if (approval.status === 'rejected') {
          await this.recordRejection(approvalRequest.id, approver.role, approval);
          return { status: 'rejected', rejectedBy: approver.role, reason: approval.reason };
        } else {
          // Timeout occurred
          return { status: 'timeout', approver: approver.role };
        }
      }
    }

    // All required approvals received
    await this.finalizeApproval(approvalRequest.id);
    return { status: 'approved' };
  }

  async sendApprovalNotification(approvalRequest, approver) {
    const users = await this.getUsersByRole(approver.role);
    
    for (const user of users) {
      // Send email notification
      await this.sendEmail({
        to: user.email,
        subject: `Approval Required: ${approvalRequest.entityType} ${approvalRequest.entityId}`,
        template: 'approval-request',
        data: {
          approvalRequest,
          approver: user,
          approvalLink: `${process.env.APPROVAL_PORTAL_URL}/approve/${approvalRequest.id}`,
          timeoutHours: approver.timeoutHours
        }
      });

      // Send in-app notification
      await this.bigledger.notifications.create({
        userId: user.id,
        type: 'approval_request',
        title: 'Approval Required',
        message: `${approvalRequest.entityType} requires your approval`,
        data: { approvalRequestId: approvalRequest.id }
      });
    }
  }

  async waitForApproval(approvalRequestId, approverRole, timeoutHours) {
    return new Promise((resolve) => {
      const timeout = setTimeout(() => {
        resolve({ status: 'timeout' });
      }, timeoutHours * 60 * 60 * 1000);

      // Listen for approval webhook
      this.context.onWebhook(`approval.${approvalRequestId}.${approverRole}`, (event) => {
        clearTimeout(timeout);
        resolve(event.data);
      });

      // Also check periodically in case webhook fails
      const checkInterval = setInterval(async () => {
        const approval = await this.checkApprovalStatus(approvalRequestId, approverRole);
        if (approval) {
          clearTimeout(timeout);
          clearInterval(checkInterval);
          resolve(approval);
        }
      }, 5 * 60 * 1000); // Check every 5 minutes
    });
  }
}

Monitoring and Analytics

Workflow Performance Monitoring

// monitoring/workflow-monitor.js
class WorkflowMonitor {
  
  constructor() {
    this.metrics = {
      workflowsStarted: new Counter('workflows_started_total', ['workflow_type', 'status']),
      workflowDuration: new Histogram('workflow_duration_seconds', ['workflow_type']),
      workflowErrors: new Counter('workflow_errors_total', ['workflow_type', 'error_type']),
      activeWorkflows: new Gauge('active_workflows', ['workflow_type'])
    };
  }

  startMonitoring() {
    // Monitor workflow health
    setInterval(() => {
      this.checkWorkflowHealth();
    }, 60000); // Every minute

    // Generate workflow analytics
    cron.schedule('0 * * * *', () => {
      this.generateWorkflowAnalytics();
    });

    // Alert on workflow failures
    this.setupFailureAlerts();
  }

  async trackWorkflowStart(workflowType, workflowId) {
    this.metrics.workflowsStarted.inc({ workflow_type: workflowType, status: 'started' });
    this.metrics.activeWorkflows.inc({ workflow_type: workflowType });
    
    await this.recordWorkflowEvent({
      workflowId,
      workflowType,
      event: 'started',
      timestamp: new Date(),
      metadata: {}
    });
  }

  async trackWorkflowCompletion(workflowType, workflowId, duration, status) {
    this.metrics.workflowsStarted.inc({ workflow_type: workflowType, status });
    this.metrics.workflowDuration.observe({ workflow_type: workflowType }, duration);
    this.metrics.activeWorkflows.dec({ workflow_type: workflowType });
    
    await this.recordWorkflowEvent({
      workflowId,
      workflowType,
      event: 'completed',
      status,
      duration,
      timestamp: new Date()
    });
  }

  async trackWorkflowError(workflowType, workflowId, error) {
    this.metrics.workflowErrors.inc({ 
      workflow_type: workflowType, 
      error_type: error.constructor.name 
    });
    
    await this.recordWorkflowEvent({
      workflowId,
      workflowType,
      event: 'error',
      error: {
        name: error.name,
        message: error.message,
        stack: error.stack
      },
      timestamp: new Date()
    });

    // Send alert for critical errors
    if (this.isCriticalError(error)) {
      await this.sendCriticalErrorAlert(workflowType, workflowId, error);
    }
  }

  async generateWorkflowAnalytics() {
    const analytics = {
      hourly: await this.getHourlyWorkflowStats(),
      daily: await this.getDailyWorkflowStats(),
      weekly: await this.getWeeklyWorkflowStats(),
      performance: await this.getPerformanceMetrics(),
      errors: await this.getErrorAnalytics()
    };

    // Store analytics
    await this.storeAnalytics(analytics);
    
    // Generate insights
    const insights = await this.generateInsights(analytics);
    
    // Send analytics report
    await this.sendAnalyticsReport(analytics, insights);
  }

  async generateInsights(analytics) {
    const insights = [];

    // Performance insights
    if (analytics.performance.averageDuration > analytics.performance.baseline * 1.5) {
      insights.push({
        type: 'performance_degradation',
        severity: 'warning',
        message: 'Workflow performance has degraded by 50% compared to baseline',
        recommendation: 'Investigate resource constraints and optimize bottlenecks'
      });
    }

    // Error rate insights
    if (analytics.errors.errorRate > 5) {
      insights.push({
        type: 'high_error_rate',
        severity: 'critical',
        message: `Error rate is ${analytics.errors.errorRate}%, above 5% threshold`,
        recommendation: 'Investigate common error patterns and implement fixes'
      });
    }

    // Volume insights
    const volumeChange = analytics.daily.today.volume / analytics.daily.yesterday.volume;
    if (volumeChange > 2) {
      insights.push({
        type: 'volume_spike',
        severity: 'info',
        message: `Workflow volume increased ${Math.round(volumeChange * 100)}% from yesterday`,
        recommendation: 'Monitor system resources and scaling requirements'
      });
    }

    return insights;
  }
}

This comprehensive automation framework enables sophisticated business process automation, from simple webhook-triggered actions to complex multi-stage approval workflows. The monitoring and analytics capabilities ensure your automations run reliably and efficiently at scale.