ScriptTask - Inline Code Execution
ScriptTask executes JavaScript/Node.js code directly within the process workflow. It has full access to process variables and can perform calculations, data transformations, and business logic.
Properties
Required Properties
Script: The JavaScript code to executeTaskType: Must be "ScriptTask"Name: Display name of the task
Optional Properties
ScriptLanguage: Language (default: "javascript")ResultVariable: Variable name to store script resultInputMapping: Map process variables to script inputsOutputMapping: Map script outputs to process variablesEntityState: State during executionAsyncBefore/AsyncAfter: Async execution flags
BPMN XML Examples
Basic Script Task
<bpmn:scriptTask id="Task_Calculate" name="Calculate Loan Metrics">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Calculate monthly payment
const monthlyRate = annualRate / 12 / 100;
const numPayments = termMonths;
monthlyPayment = loanAmount *
(monthlyRate * Math.pow(1 + monthlyRate, numPayments)) /
(Math.pow(1 + monthlyRate, numPayments) - 1);
totalPayment = monthlyPayment * numPayments;
totalInterest = totalPayment - loanAmount;
console.log('Monthly Payment:', monthlyPayment);
" />
<custom:property name="ScriptLanguage" value="javascript" />
</custom:properties>
</bpmn:extensionElements>
<bpmn:incoming>Flow_In</bpmn:incoming>
<bpmn:outgoing>Flow_Out</bpmn:outgoing>
</bpmn:scriptTask>
Script with Result Variable
<bpmn:scriptTask id="Task_ComputeScore" name="Calculate Credit Score">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Calculate credit score based on multiple factors
let score = 600; // Base score
// Payment history (35%)
if (onTimePayments > 95) score += 100;
else if (onTimePayments > 80) score += 50;
// Credit utilization (30%)
const utilization = creditUsed / creditLimit * 100;
if (utilization < 30) score += 80;
else if (utilization < 50) score += 40;
else score -= 20;
// Length of credit history (15%)
if (accountAgeMonths > 60) score += 40;
else if (accountAgeMonths > 24) score += 20;
// Cap at 850
score = Math.min(score, 850);
return score;
" />
<custom:property name="ResultVariable" value="creditScore" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
Script with Input/Output Mapping
<bpmn:scriptTask id="Task_ValidateData" name="Validate Application Data">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Validate input data
const errors = [];
if (!customerName || customerName.trim() === '') {
errors.push('Customer name is required');
}
if (!email || !email.includes('@')) {
errors.push('Valid email is required');
}
if (amount <= 0 || amount > 1000000) {
errors.push('Loan amount must be between $1 and $1,000,000');
}
if (termMonths < 12 || termMonths > 360) {
errors.push('Term must be between 12 and 360 months');
}
// Set validation results
isValid = errors.length === 0;
validationErrors = errors;
validatedAt = new Date().toISOString();
" />
<!-- Map process variables to script inputs -->
<custom:property name="InputMapping" value="{
"customerName": "application.customerName",
"email": "application.email",
"amount": "application.loanAmount",
"termMonths": "application.term"
}" />
<!-- Map script outputs to process variables -->
<custom:property name="OutputMapping" value="{
"validationResult": "isValid",
"validationErrors": "errors",
"validationTimestamp": "validatedAt"
}" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
Script with BankLingo Commands
<bpmn:scriptTask id="Task_EnrichData" name="Enrich Customer Data">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Use BankLingo commands to fetch additional data
// Get customer details
const customerDetails = doCmd('GetCustomerById', { customerId });
customerName = customerDetails.fullName;
customerSegment = customerDetails.segment;
// Get account balance
const accountInfo = doCmd('GetAccountBalance', {
accountNumber: customerDetails.primaryAccount
});
availableBalance = accountInfo.availableBalance;
// Calculate debt-to-income ratio
const monthlyIncome = customerDetails.monthlyIncome;
const existingDebts = doCmd('GetCustomerDebts', { customerId });
const totalMonthlyDebt = existingDebts.reduce((sum, debt) => sum + debt.monthlyPayment, 0);
debtToIncomeRatio = (totalMonthlyDebt / monthlyIncome) * 100;
// Set risk category
if (debtToIncomeRatio > 43) {
riskCategory = 'High';
} else if (debtToIncomeRatio > 36) {
riskCategory = 'Medium';
} else {
riskCategory = 'Low';
}
console.log('Customer enriched:', customerName, 'Risk:', riskCategory);
" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
Conditional Logic Script
<bpmn:scriptTask id="Task_DetermineApprovalPath" name="Determine Approval Requirements">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Determine which approval path to take based on business rules
let requiredApprovers = [];
let approvalLevel = 'Standard';
// High amount requires senior approval
if (loanAmount > 100000) {
requiredApprovers.push('SeniorOfficer');
approvalLevel = 'Senior';
}
// High risk requires additional approval
if (riskScore > 70 || debtToIncomeRatio > 40) {
requiredApprovers.push('RiskManager');
approvalLevel = 'Enhanced';
}
// New customers need compliance check
if (customerTenureMonths < 12) {
requiredApprovers.push('ComplianceOfficer');
}
// VIP customers get fast track
if (customerSegment === 'VIP' && loanAmount < 50000) {
requiredApprovers = ['LoanOfficer'];
approvalLevel = 'FastTrack';
}
// Set variables for gateway routing
approvalPath = approvalLevel;
approversNeeded = requiredApprovers.join(',');
estimatedApprovalDays = requiredApprovers.length * 2;
" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
Accessing Process Variables
Scripts have direct access to all process variables:
// Read variables
const amount = loanAmount; // Direct access
const customer = customerData.name; // Nested access
// Set variables
approvalStatus = 'Approved';
processedDate = new Date().toISOString();
// Modify variables
loanAmount = loanAmount * 1.1; // Add 10% fee
customerData.verified = true;
Using BankLingo Commands
Call any BankLingo command using doCmd():
// Execute command
const result = doCmd('CommandName', {
parameter1: value1,
parameter2: value2
});
// Example: Get account balance
const balance = doCmd('GetAccountBalance', {
accountNumber: accountNumber
});
availableBalance = balance.availableBalance;
// Example: Create transaction
const transaction = doCmd('CreateTransaction', {
accountId: accountId,
amount: amount,
type: 'Debit',
description: 'Loan disbursement'
});
transactionId = transaction.id;
Use Cases
1. Data Transformation
Transform data between different formats:
<bpmn:scriptTask id="Task_Transform" name="Transform Data">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Transform customer data from external format to internal format
internalCustomer = {
id: externalData.customer_id,
fullName: externalData.first_name + ' ' + externalData.last_name,
email: externalData.contact.email,
phone: externalData.contact.phone,
address: {
street: externalData.address.street_address,
city: externalData.address.city,
state: externalData.address.state_code,
zip: externalData.address.postal_code
},
dateOfBirth: new Date(externalData.dob).toISOString()
};
" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
2. Complex Calculations
Perform business calculations:
<bpmn:scriptTask id="Task_PricingCalculation" name="Calculate Loan Pricing">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Base rate from market
let interestRate = baseMarketRate;
// Adjust for credit score
if (creditScore < 600) interestRate += 5.0;
else if (creditScore < 650) interestRate += 3.0;
else if (creditScore < 700) interestRate += 1.5;
else if (creditScore >= 750) interestRate -= 0.5;
// Adjust for loan-to-value ratio
const ltvRatio = loanAmount / collateralValue * 100;
if (ltvRatio > 80) interestRate += 1.0;
else if (ltvRatio > 90) interestRate += 2.0;
// Adjust for relationship
if (customerTenureYears > 5) interestRate -= 0.25;
if (numberOfAccounts > 3) interestRate -= 0.15;
// Calculate final numbers
finalInterestRate = Math.max(interestRate, minimumRate);
monthlyPayment = calculatePayment(loanAmount, finalInterestRate, termMonths);
totalInterest = (monthlyPayment * termMonths) - loanAmount;
aprRate = calculateAPR(loanAmount, finalInterestRate, fees, termMonths);
" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
3. Validation
Validate business rules:
<bpmn:scriptTask id="Task_BusinessRules" name="Validate Business Rules">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
const violations = [];
// Rule 1: Debt-to-income ratio
if (debtToIncomeRatio > 43) {
violations.push({
rule: 'DTI_LIMIT',
message: 'Debt-to-income ratio exceeds maximum of 43%',
severity: 'Critical'
});
}
// Rule 2: Minimum credit score
if (creditScore < 580) {
violations.push({
rule: 'MIN_CREDIT_SCORE',
message: 'Credit score below minimum requirement',
severity: 'Critical'
});
}
// Rule 3: Maximum loan amount
const maxLoan = annualIncome * 3;
if (loanAmount > maxLoan) {
violations.push({
rule: 'MAX_LOAN_AMOUNT',
message: 'Loan amount exceeds 3x annual income',
severity: 'High'
});
}
// Rule 4: Recent bankruptcies
if (bankruptcyInLast7Years) {
violations.push({
rule: 'BANKRUPTCY_HISTORY',
message: 'Recent bankruptcy on record',
severity: 'High'
});
}
ruleViolations = violations;
rulesPass = violations.length === 0;
criticalViolations = violations.filter(v => v.severity === 'Critical').length;
" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
4. Decision Logic
Make routing decisions:
<bpmn:scriptTask id="Task_RouteDecision" name="Determine Routing">
<bpmn:extensionElements>
<custom:properties>
<custom:property name="Script" value="
// Complex routing decision
// Check for auto-approval eligibility
const autoApproveEligible =
creditScore >= 720 &&
debtToIncomeRatio < 36 &&
loanAmount <= 25000 &&
customerTenureMonths >= 24 &&
!recentDelinquencies;
if (autoApproveEligible) {
routingDecision = 'AutoApprove';
return;
}
// Check for auto-rejection
const autoRejectCriteria =
creditScore < 580 ||
debtToIncomeRatio > 50 ||
recentBankruptcy ||
fraudFlagged;
if (autoRejectCriteria) {
routingDecision = 'AutoReject';
rejectionReason = 'Does not meet minimum criteria';
return;
}
// Needs manual review - determine level
if (loanAmount > 100000 || riskScore > 70) {
routingDecision = 'SeniorReview';
} else {
routingDecision = 'StandardReview';
}
" />
<custom:property name="ResultVariable" value="routingDecision" />
</custom:properties>
</bpmn:extensionElements>
</bpmn:scriptTask>
Best Practices
1. Keep Scripts Focused
Each script task should do one thing well:
<!-- Good - Single responsibility -->
<bpmn:scriptTask id="Task_Calculate" name="Calculate Metrics" />
<bpmn:scriptTask id="Task_Validate" name="Validate Results" />
<!-- Avoid - Doing too much -->
<bpmn:scriptTask id="Task_Everything" name="Calculate, Validate, and Route" />
2. Handle Errors
Use try-catch for error handling:
try {
const result = doCmd('SomeCommand', { params });
processedResult = result.data;
} catch (error) {
console.error('Command failed:', error.message);
errorOccurred = true;
errorMessage = error.message;
}
3. Log for Debugging
Use console.log for debugging:
console.log('Starting calculation with amount:', loanAmount);
const result = calculateSomething();
console.log('Calculation result:', result);
4. Use Meaningful Variable Names
// Good
const monthlyPayment = calculatePayment(principal, rate, term);
const annualPercentageRate = calculateAPR(amount, rate, fees);
// Avoid
const mp = calc(p, r, t);
const apr = calcAPR(a, r, f);
5. Comment Complex Logic
// Calculate effective interest rate
// Formula: ((1 + r/n)^n) - 1
// Where r = nominal rate, n = compounding periods
const effectiveRate = Math.pow(1 + (nominalRate / compoundingPeriods), compoundingPeriods) - 1;
Performance Considerations
- Keep scripts lightweight: Complex calculations should use ServiceTask to call APIs
- Avoid loops over large datasets: Use BankLingo commands for bulk operations
- Cache repeated calculations: Store results in variables
- Use async for long operations: Set
AsyncAfter="true"for better performance
Next Steps
- ServiceTask - Call external APIs
- Gateway - Route based on script results
- Variables & I/O - Advanced data handling