Add Travel Reimbursement Workflow Documentation and Enhance Component Functionality
- Introduced comprehensive documentation for the Travel Reimbursement Workflow, detailing form components, process definitions, and business rules. - Added new custom scripts for the Manager Approval Form and Travel Reimbursement Form to enhance dynamic behavior and validation. - Updated the ComponentPreview component to include a new prop for field states, improving state management during previews. - Created JSON files for the Manager Approval Form and Travel Reimbursement Form, defining their structure and validation rules. - Implemented a new process definition for the travel workflow, outlining the steps and decision points for claim processing. - Established global variables for managing workflow data, ensuring consistency and accessibility across the process.
This commit is contained in:
parent
4c67a79be0
commit
b1fc3d027a
@ -524,6 +524,10 @@ const props = defineProps({
|
||||
isPreview: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
fieldStates: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
|
350
content/documentation/07-travel-reimbursement-workflow.md
Normal file
350
content/documentation/07-travel-reimbursement-workflow.md
Normal file
@ -0,0 +1,350 @@
|
||||
# Travel Reimbursement Workflow Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The Travel Reimbursement Workflow is a comprehensive business process management solution designed to handle employee travel expense claims from submission to final approval and reimbursement. This workflow demonstrates the full capabilities of the Corrad ProcessMaker platform, including form integration, automated calculations, decision gateways, API integrations, and multi-level approval processes.
|
||||
|
||||
## Workflow Components
|
||||
|
||||
### 1. Form Components
|
||||
|
||||
#### Employee Travel Reimbursement Form
|
||||
- **Purpose**: Primary form for employees to submit travel expense claims
|
||||
- **File Location**: `docs/json/form/travel-reimbursement-form.json`
|
||||
- **Key Features**:
|
||||
- Employee details collection (name, email, department)
|
||||
- Trip information (purpose, destination, dates)
|
||||
- Detailed expense breakdown with receipt uploads
|
||||
- Automatic cost calculations
|
||||
- Policy compliance validation
|
||||
|
||||
**Form Fields Structure**:
|
||||
```
|
||||
Employee Details:
|
||||
├── Employee Name (span 6) - Required
|
||||
├── Employee Email (span 6) - Required
|
||||
└── Department (span 12) - Required
|
||||
|
||||
Trip Information:
|
||||
├── Trip Purpose (span 6) - Required
|
||||
├── Destination (span 6) - Required
|
||||
├── Start Date (span 6) - Required
|
||||
└── End Date (span 6) - Required
|
||||
|
||||
Expense Categories:
|
||||
├── Transportation Costs (span 4)
|
||||
├── Accommodation Costs (span 4)
|
||||
├── Meal Costs (span 4)
|
||||
├── Other Expenses (span 12)
|
||||
└── Receipt Upload (span 12) - File upload
|
||||
|
||||
Calculations:
|
||||
├── Total Cost (span 6) - Auto-calculated
|
||||
├── Policy Limit (span 6) - Auto-populated
|
||||
└── Over Budget Amount (span 12) - Auto-calculated
|
||||
```
|
||||
|
||||
#### Manager Approval Form
|
||||
- **Purpose**: Manager review and approval interface
|
||||
- **File Location**: `docs/json/form/manager-approval-form.json`
|
||||
- **Key Features**:
|
||||
- Display of employee claim details
|
||||
- Cost analysis with policy comparisons
|
||||
- Approval decision options (approve full, approve policy limit, reject)
|
||||
- Custom amount approval capability
|
||||
- Required justification comments
|
||||
|
||||
**Approval Options**:
|
||||
- **Approve Full Amount**: Manager approves the entire claimed amount
|
||||
- **Approve Policy Limit Only**: Manager approves only up to policy limits
|
||||
- **Reject Claim**: Manager rejects the entire claim with required explanation
|
||||
|
||||
### 2. Process Workflow
|
||||
|
||||
#### Process Definition
|
||||
- **File Location**: `docs/json/process-builder/travel-workflow-process.json`
|
||||
- **Total Nodes**: 10 nodes with 12 connecting edges
|
||||
- **Process Type**: Sequential workflow with decision gateways
|
||||
|
||||
#### Workflow Steps
|
||||
|
||||
1. **Start Node** (`start_travel_claim`)
|
||||
- Initiates the travel reimbursement process
|
||||
- Sets up initial process variables
|
||||
|
||||
2. **Employee Form Submission** (`employee_form_task`)
|
||||
- **Type**: Form Task
|
||||
- **Form**: Travel Reimbursement Form
|
||||
- **Assigned Roles**: Employee, HR
|
||||
- **Input/Output Mappings**: Maps form data to process variables
|
||||
- **Validation**: Ensures all required fields are completed
|
||||
|
||||
3. **Calculate Total Costs** (`calculate_costs_script`)
|
||||
- **Type**: Script Task
|
||||
- **Function**: Automatic calculation of total expenses
|
||||
- **Calculations**:
|
||||
```javascript
|
||||
totalCost = transportation + accommodation + meals + otherExpenses
|
||||
overBudgetAmount = Math.max(0, totalCost - policyLimit)
|
||||
isOverBudget = totalCost > policyLimit
|
||||
```
|
||||
|
||||
4. **Validate Policy Compliance** (`validate_policy_api`)
|
||||
- **Type**: API Call
|
||||
- **Endpoint**: `/api/travel/validate-policy`
|
||||
- **Method**: POST
|
||||
- **Purpose**: Check claim against company travel policies
|
||||
- **Output**: Policy validation results and limits
|
||||
|
||||
5. **Budget Decision Gateway** (`budget_decision_gateway`)
|
||||
- **Type**: Decision Gateway
|
||||
- **Logic**: Determines approval path based on budget compliance
|
||||
- **Conditions**:
|
||||
- If `totalCost <= policyLimit` → Direct to Finance Processing
|
||||
- If `totalCost > policyLimit` → Route to Manager Approval
|
||||
|
||||
6. **Manager Approval Task** (`manager_approval_task`)
|
||||
- **Type**: Form Task
|
||||
- **Form**: Manager Approval Form
|
||||
- **Assigned Roles**: Manager, Department Head
|
||||
- **Trigger**: Only when claim exceeds policy limits
|
||||
- **Decision Options**: Approve/Modify/Reject
|
||||
|
||||
7. **Approval Decision Gateway** (`approval_decision_gateway`)
|
||||
- **Type**: Decision Gateway
|
||||
- **Logic**: Routes based on manager's decision
|
||||
- **Paths**:
|
||||
- Approved → Finance Processing
|
||||
- Rejected → Employee Notification (Rejection)
|
||||
- Modified → Finance Processing with adjusted amount
|
||||
|
||||
8. **Finance Processing** (`finance_processing_api`)
|
||||
- **Type**: API Call
|
||||
- **Endpoint**: `/api/finance/process-reimbursement`
|
||||
- **Method**: POST
|
||||
- **Function**: Initiates payment processing
|
||||
- **Data**: Final approved amount and employee details
|
||||
|
||||
9. **Send Notification** (`send_final_notification`)
|
||||
- **Type**: Notification Task
|
||||
- **Recipients**: Employee, Manager, Finance Team
|
||||
- **Content**: Final decision and payment status
|
||||
- **Channels**: Email, In-app notification
|
||||
|
||||
10. **End Process** (`end_travel_claim`)
|
||||
- **Type**: End Node
|
||||
- **Function**: Completes the workflow
|
||||
- **Final Status**: Sets process completion status
|
||||
|
||||
### 3. Process Variables
|
||||
|
||||
**File Location**: `docs/json/process-builder/travel-workflow-variables.json`
|
||||
|
||||
#### Employee Information Variables
|
||||
```javascript
|
||||
employeeName: String // Employee's full name
|
||||
employeeEmail: String // Employee's email address
|
||||
department: String // Employee's department
|
||||
employeeId: String // System employee identifier
|
||||
```
|
||||
|
||||
#### Trip Details Variables
|
||||
```javascript
|
||||
tripPurpose: String // Purpose of business trip
|
||||
destination: String // Travel destination
|
||||
startDate: Date // Trip start date
|
||||
endDate: Date // Trip end date
|
||||
tripDuration: Number // Calculated trip duration in days
|
||||
```
|
||||
|
||||
#### Financial Variables
|
||||
```javascript
|
||||
transportationCost: Number // Transportation expenses
|
||||
accommodationCost: Number // Hotel/lodging expenses
|
||||
mealCost: Number // Meal expenses
|
||||
otherExpenses: Number // Miscellaneous expenses
|
||||
totalCost: Number // Sum of all expenses
|
||||
policyLimit: Number // Company policy limit
|
||||
overBudgetAmount: Number // Amount exceeding policy
|
||||
isOverBudget: Boolean // Budget compliance flag
|
||||
approvedAmount: Number // Final approved amount
|
||||
```
|
||||
|
||||
#### Process Control Variables
|
||||
```javascript
|
||||
processStatus: String // Current process status
|
||||
currentStep: String // Current workflow step
|
||||
approvalRequired: Boolean // Manager approval needed flag
|
||||
managerDecision: String // Manager's approval decision
|
||||
rejectionReason: String // Reason for rejection (if applicable)
|
||||
paymentStatus: String // Finance processing status
|
||||
```
|
||||
|
||||
### 4. Dynamic Form Behavior
|
||||
|
||||
#### Travel Reimbursement Form Script
|
||||
**File Location**: `docs/json/form/travel-reimbursement-customScript.js`
|
||||
|
||||
**Key Functions**:
|
||||
1. **Auto-calculation Engine**:
|
||||
```javascript
|
||||
// Real-time cost calculation
|
||||
onFieldChange(['transportation_cost', 'accommodation_cost', 'meal_cost', 'other_expenses'], () => {
|
||||
const total = calculateTotalCost();
|
||||
setField('total_cost', total);
|
||||
checkPolicyCompliance(total);
|
||||
});
|
||||
```
|
||||
|
||||
2. **Policy Validation**:
|
||||
```javascript
|
||||
// Check against policy limits
|
||||
const checkPolicyCompliance = (totalCost) => {
|
||||
const policyLimit = getField('policy_limit') || 2000;
|
||||
const overBudget = Math.max(0, totalCost - policyLimit);
|
||||
|
||||
if (overBudget > 0) {
|
||||
showError(`Amount exceeds policy limit by RM${overBudget.toFixed(2)}`);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
3. **Date Validation**:
|
||||
```javascript
|
||||
// Ensure end date is after start date
|
||||
onFieldChange(['start_date', 'end_date'], () => {
|
||||
validateDateRange();
|
||||
});
|
||||
```
|
||||
|
||||
#### Manager Approval Form Script
|
||||
**File Location**: `docs/json/form/manager-approval-customScript.js`
|
||||
|
||||
**Key Functions**:
|
||||
1. **Decision Logic**:
|
||||
```javascript
|
||||
onFieldChange('manager_decision', (decision) => {
|
||||
switch(decision) {
|
||||
case 'approve_full':
|
||||
showField('custom_approved_amount');
|
||||
break;
|
||||
case 'approve_policy':
|
||||
hideField('custom_approved_amount');
|
||||
break;
|
||||
case 'reject':
|
||||
showError('Please provide detailed rejection comments');
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
2. **Amount Validation**:
|
||||
```javascript
|
||||
// Validate custom approved amounts
|
||||
const validateCustomAmount = (amount) => {
|
||||
const totalClaimed = parseFloat(getField('total_cost_display'));
|
||||
if (amount > totalClaimed) {
|
||||
showError('Approved amount cannot exceed claimed amount');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
## Business Rules
|
||||
|
||||
### Policy Compliance Rules
|
||||
1. **Transportation**: Maximum RM800 per trip
|
||||
2. **Accommodation**: Maximum RM300 per night
|
||||
3. **Meals**: Maximum RM100 per day
|
||||
4. **Total Trip Limit**: RM2000 per trip
|
||||
5. **Receipt Requirement**: All expenses > RM50 require receipts
|
||||
|
||||
### Approval Authority
|
||||
1. **Under Policy Limit**: Automatic approval, direct to finance
|
||||
2. **Over Policy Limit**: Requires manager approval
|
||||
3. **Over RM5000**: Requires department head approval
|
||||
4. **International Travel**: Additional CEO approval required
|
||||
|
||||
### Notification Rules
|
||||
1. **Employee**: Notified at submission, approval, and payment stages
|
||||
2. **Manager**: Notified when approval required and decision made
|
||||
3. **Finance**: Notified for all approved claims requiring payment
|
||||
4. **HR**: Notified for audit trail and policy compliance tracking
|
||||
|
||||
## Integration Points
|
||||
|
||||
### API Endpoints
|
||||
1. **Policy Validation**: `/api/travel/validate-policy`
|
||||
2. **Finance Processing**: `/api/finance/process-reimbursement`
|
||||
3. **Employee Directory**: `/api/employees/lookup`
|
||||
4. **Notification Service**: `/api/notifications/send`
|
||||
|
||||
### External Systems
|
||||
1. **HR Management System**: Employee data synchronization
|
||||
2. **Finance ERP**: Payment processing integration
|
||||
3. **Document Management**: Receipt storage and retrieval
|
||||
4. **Email Service**: Automated notification delivery
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
### For Employees
|
||||
1. Navigate to the Travel Reimbursement workflow
|
||||
2. Fill out the employee travel reimbursement form
|
||||
3. Upload receipts for expenses over RM50
|
||||
4. Review calculated totals and policy compliance
|
||||
5. Submit the form for processing
|
||||
6. Monitor approval status via notifications
|
||||
|
||||
### For Managers
|
||||
1. Receive notification when approval is required
|
||||
2. Review employee claim details and supporting documents
|
||||
3. Make approval decision based on policy and business needs
|
||||
4. Provide justification comments for decisions
|
||||
5. Submit approval decision
|
||||
|
||||
### For Finance Team
|
||||
1. Receive approved claims for payment processing
|
||||
2. Verify all required documentation is present
|
||||
3. Process payment through ERP system
|
||||
4. Update payment status in the workflow
|
||||
5. Send final confirmation to employee
|
||||
|
||||
## Monitoring and Analytics
|
||||
|
||||
### Process Metrics
|
||||
- Average processing time per claim
|
||||
- Approval rates by manager and department
|
||||
- Policy compliance statistics
|
||||
- Cost analysis and trending
|
||||
|
||||
### Performance Indicators
|
||||
- Claims processed per month
|
||||
- Average claim value
|
||||
- Rejection rates and reasons
|
||||
- Processing bottlenecks identification
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
1. **Form Validation Errors**: Check required fields and data formats
|
||||
2. **File Upload Problems**: Ensure receipts are in supported formats (PDF, JPG, PNG)
|
||||
3. **Calculation Errors**: Verify numeric input formats and policy limits
|
||||
4. **Approval Delays**: Check manager notification settings and availability
|
||||
|
||||
### Error Handling
|
||||
- Form validation provides real-time feedback
|
||||
- API failures trigger retry mechanisms
|
||||
- Email delivery failures logged for manual follow-up
|
||||
- Process timeouts escalate to supervisors
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
1. **Mobile App Integration**: Native mobile form submission
|
||||
2. **OCR Receipt Processing**: Automatic expense extraction from receipts
|
||||
3. **Multi-currency Support**: International travel expense handling
|
||||
4. **Advanced Analytics**: Predictive spending analysis
|
||||
5. **Integration Expansion**: Additional ERP and HR system connectors
|
||||
|
||||
This comprehensive travel reimbursement workflow demonstrates the full capabilities of the Corrad ProcessMaker platform, providing a real-world example of how complex business processes can be automated, managed, and optimized through digital workflow solutions.
|
232
docs/json/form/manager-approval-customScript.js
Normal file
232
docs/json/form/manager-approval-customScript.js
Normal file
@ -0,0 +1,232 @@
|
||||
// Manager Approval Form Custom Script Engine
|
||||
// This script provides dynamic behavior for the manager approval form
|
||||
|
||||
console.log('Manager Approval Form Script Loaded');
|
||||
|
||||
// Auto-set approval date to today
|
||||
const setApprovalDate = () => {
|
||||
const today = new Date();
|
||||
const formattedDate = today.toISOString().split('T')[0]; // YYYY-MM-DD format
|
||||
setField('approval_date', formattedDate);
|
||||
};
|
||||
|
||||
// Calculate and display recommended approval amounts
|
||||
const calculateRecommendedAmounts = () => {
|
||||
const totalClaimed = parseFloat(getField('total_cost_display')) || 0;
|
||||
const policyLimit = parseFloat(getField('policy_limit_display')) || 0;
|
||||
const overBudget = parseFloat(getField('over_budget_amount_display')) || 0;
|
||||
|
||||
if (totalClaimed > 0 && policyLimit > 0) {
|
||||
const percentageOver = ((overBudget / policyLimit) * 100).toFixed(1);
|
||||
showInfo(`This claim is ${percentageOver}% over the policy limit. Policy limit: RM${policyLimit.toFixed(2)}, Claimed: RM${totalClaimed.toFixed(2)}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Validate manager decision and provide guidance
|
||||
const handleDecisionChange = (decision) => {
|
||||
const totalClaimed = parseFloat(getField('total_cost_display')) || 0;
|
||||
const policyLimit = parseFloat(getField('policy_limit_display')) || 0;
|
||||
|
||||
console.log('Manager decision changed to:', decision);
|
||||
|
||||
switch(decision) {
|
||||
case 'approve_full':
|
||||
showField('custom_approved_amount');
|
||||
showInfo(`💰 Approving full amount: RM${totalClaimed.toFixed(2)}. You can enter a custom amount if needed.`);
|
||||
setField('custom_approved_amount', totalClaimed.toString());
|
||||
break;
|
||||
|
||||
case 'approve_policy':
|
||||
hideField('custom_approved_amount');
|
||||
showInfo(`⚖️ Approving policy limit only: RM${policyLimit.toFixed(2)}. Employee will be notified of the reduced amount.`);
|
||||
break;
|
||||
|
||||
case 'reject':
|
||||
hideField('custom_approved_amount');
|
||||
showError('❌ Claim will be rejected. Please provide detailed comments explaining the rejection reason.');
|
||||
break;
|
||||
|
||||
default:
|
||||
hideField('custom_approved_amount');
|
||||
}
|
||||
};
|
||||
|
||||
// Validate custom approved amount
|
||||
const validateCustomAmount = (amount) => {
|
||||
const numAmount = parseFloat(amount) || 0;
|
||||
const totalClaimed = parseFloat(getField('total_cost_display')) || 0;
|
||||
const policyLimit = parseFloat(getField('policy_limit_display')) || 0;
|
||||
|
||||
if (numAmount < 0) {
|
||||
showError('Approved amount cannot be negative');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numAmount > totalClaimed) {
|
||||
showError(`Approved amount (RM${numAmount}) cannot exceed claimed amount (RM${totalClaimed})`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numAmount > 0 && numAmount < policyLimit) {
|
||||
showInfo(`Custom amount (RM${numAmount}) is less than policy limit (RM${policyLimit}). Consider approving policy limit instead.`);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Validate manager comments based on decision
|
||||
const validateComments = (comments, decision) => {
|
||||
if (!comments || comments.trim().length < 10) {
|
||||
showError('Please provide detailed comments (minimum 10 characters)');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decision === 'reject' && comments.length < 50) {
|
||||
showError('Rejection requires detailed explanation (minimum 50 characters)');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decision === 'approve_full') {
|
||||
const totalClaimed = parseFloat(getField('total_cost_display')) || 0;
|
||||
const policyLimit = parseFloat(getField('policy_limit_display')) || 0;
|
||||
const overBudget = totalClaimed - policyLimit;
|
||||
|
||||
if (overBudget > 500 && !comments.toLowerCase().includes('business')) {
|
||||
showInfo('Consider mentioning business justification for approving over-budget amounts > RM500');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Format manager name (title case)
|
||||
const formatManagerName = (name) => {
|
||||
if (!name || typeof name !== 'string') return name;
|
||||
|
||||
return name
|
||||
.toLowerCase()
|
||||
.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
// Form validation before submission
|
||||
const validateApprovalForm = () => {
|
||||
let isValid = true;
|
||||
const errors = [];
|
||||
|
||||
// Check required fields
|
||||
const requiredFields = {
|
||||
'manager_decision': 'Approval Decision',
|
||||
'manager_comments': 'Manager Comments',
|
||||
'manager_name': 'Manager Name',
|
||||
'approval_date': 'Approval Date'
|
||||
};
|
||||
|
||||
Object.entries(requiredFields).forEach(([fieldName, displayName]) => {
|
||||
const value = getField(fieldName);
|
||||
if (!value || value.toString().trim() === '') {
|
||||
errors.push(`${displayName} is required`);
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Validate decision-specific requirements
|
||||
const decision = getField('manager_decision');
|
||||
const comments = getField('manager_comments');
|
||||
const customAmount = getField('custom_approved_amount');
|
||||
|
||||
if (decision && comments) {
|
||||
if (!validateComments(comments, decision)) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (decision === 'approve_full' && customAmount) {
|
||||
if (!validateCustomAmount(customAmount)) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate approval date is not in the future
|
||||
const approvalDate = getField('approval_date');
|
||||
if (approvalDate) {
|
||||
const selectedDate = new Date(approvalDate);
|
||||
const today = new Date();
|
||||
today.setHours(23, 59, 59, 999); // End of today
|
||||
|
||||
if (selectedDate > today) {
|
||||
errors.push('Approval date cannot be in the future');
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Show validation results
|
||||
if (errors.length > 0) {
|
||||
showError(`Please fix the following errors:\n• ${errors.join('\n• ')}`);
|
||||
} else {
|
||||
showSuccess('Form validation passed! Ready to submit approval decision.');
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
// Set up field change handlers
|
||||
onFieldChange('manager_decision', (newValue) => {
|
||||
if (newValue) {
|
||||
handleDecisionChange(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('custom_approved_amount', (newValue) => {
|
||||
if (newValue) {
|
||||
validateCustomAmount(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('manager_comments', (newValue) => {
|
||||
const decision = getField('manager_decision');
|
||||
if (newValue && decision) {
|
||||
validateComments(newValue, decision);
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('manager_name', (newValue) => {
|
||||
if (newValue && typeof newValue === 'string') {
|
||||
const formatted = formatManagerName(newValue);
|
||||
if (formatted !== newValue) {
|
||||
setTimeout(() => {
|
||||
setField('manager_name', formatted);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize form on load
|
||||
setTimeout(() => {
|
||||
setApprovalDate();
|
||||
calculateRecommendedAmounts();
|
||||
|
||||
// Make display fields read-only by adding visual styling
|
||||
const displayFields = [
|
||||
'claim_summary', 'employee_name_display', 'department_display',
|
||||
'trip_purpose_display', 'destination_display', 'total_cost_display',
|
||||
'policy_limit_display', 'over_budget_amount_display'
|
||||
];
|
||||
|
||||
displayFields.forEach(fieldName => {
|
||||
const fieldElement = document.querySelector(`[data-name="${fieldName}"] input`);
|
||||
if (fieldElement) {
|
||||
fieldElement.style.backgroundColor = '#f3f4f6';
|
||||
fieldElement.style.cursor = 'not-allowed';
|
||||
fieldElement.readOnly = true;
|
||||
}
|
||||
});
|
||||
|
||||
showInfo('Review the claim details above and make your approval decision below.');
|
||||
}, 1000);
|
||||
|
||||
// Expose validation function for form submission
|
||||
window.validateManagerApprovalForm = validateApprovalForm;
|
||||
|
||||
console.log('Manager Approval Form Script initialized successfully');
|
408
docs/json/form/manager-approval-form.json
Normal file
408
docs/json/form/manager-approval-form.json
Normal file
@ -0,0 +1,408 @@
|
||||
[
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_manager_approval",
|
||||
"level": 2,
|
||||
"value": "TRAVEL CLAIM MANAGER APPROVAL",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"name": "paragraph_approval_instructions",
|
||||
"value": "This travel claim exceeds policy limits and requires your approval. Please review the details below and make your decision.",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_claim_details",
|
||||
"level": 3,
|
||||
"value": "Claim Details",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Summary of the travel claim",
|
||||
"name": "claim_summary",
|
||||
"type": "text",
|
||||
"label": "Claim Summary",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"placeholder": "Claim summary will be populated from process",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Employee who submitted the claim",
|
||||
"name": "employee_name_display",
|
||||
"type": "text",
|
||||
"label": "Employee Name",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Employee name",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Employee's department",
|
||||
"name": "department_display",
|
||||
"type": "text",
|
||||
"label": "Department",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Department",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Purpose of the business trip",
|
||||
"name": "trip_purpose_display",
|
||||
"type": "text",
|
||||
"label": "Trip Purpose",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Trip purpose",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Travel destination",
|
||||
"name": "destination_display",
|
||||
"type": "text",
|
||||
"label": "Destination",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "",
|
||||
"placeholder": "Destination",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_cost_breakdown",
|
||||
"level": 3,
|
||||
"value": "Cost Analysis",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Total amount claimed by employee",
|
||||
"name": "total_cost_display",
|
||||
"type": "text",
|
||||
"label": "Total Claimed Amount (RM)",
|
||||
"width": "33.33%",
|
||||
"gridColumn": "span 4",
|
||||
"validation": "",
|
||||
"placeholder": "0.00",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Maximum allowed per company policy",
|
||||
"name": "policy_limit_display",
|
||||
"type": "text",
|
||||
"label": "Policy Limit (RM)",
|
||||
"width": "33.33%",
|
||||
"gridColumn": "span 4",
|
||||
"validation": "",
|
||||
"placeholder": "0.00",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Amount exceeding policy limits",
|
||||
"name": "over_budget_amount_display",
|
||||
"type": "text",
|
||||
"label": "Over Budget Amount (RM)",
|
||||
"width": "33.33%",
|
||||
"gridColumn": "span 4",
|
||||
"validation": "",
|
||||
"placeholder": "0.00",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"name": "paragraph_policy_note",
|
||||
"value": "⚠️ This claim exceeds company travel policy limits. As a manager, you can approve the full amount, approve only the policy limit, or reject the claim entirely.",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_manager_decision",
|
||||
"level": 3,
|
||||
"value": "Manager Decision",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "radio",
|
||||
"props": {
|
||||
"help": "Select your approval decision",
|
||||
"name": "manager_decision",
|
||||
"type": "radio",
|
||||
"label": "Approval Decision",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "✅ Approve Full Amount - Employee will receive the full claimed amount", "value": "approve_full" },
|
||||
{ "label": "⚠️ Approve Policy Limit Only - Employee will receive only the policy-allowed amount", "value": "approve_policy" },
|
||||
{ "label": "❌ Reject Claim - Claim will be denied entirely", "value": "reject" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter the approved amount if different from the options above",
|
||||
"name": "custom_approved_amount",
|
||||
"type": "number",
|
||||
"label": "Custom Approved Amount (RM) - Optional",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "min:0",
|
||||
"placeholder": "Enter custom amount",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": true,
|
||||
"operator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"field": "manager_decision",
|
||||
"operator": "eq",
|
||||
"value": "approve_full"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Provide justification for your decision",
|
||||
"name": "manager_comments",
|
||||
"type": "text",
|
||||
"label": "Manager Comments",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"placeholder": "Please provide comments explaining your decision...",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_additional_info",
|
||||
"level": 3,
|
||||
"value": "Additional Information",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"props": {
|
||||
"help": "Check any additional requirements for this approval",
|
||||
"name": "additional_requirements",
|
||||
"type": "checkbox",
|
||||
"label": "Additional Requirements",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Require additional documentation from employee", "value": "require_docs" },
|
||||
{ "label": "Schedule follow-up meeting with employee", "value": "follow_up" },
|
||||
{ "label": "Flag for finance team review", "value": "finance_review" },
|
||||
{ "label": "Add to employee's travel record for future reference", "value": "record_flag" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter your name as the approving manager",
|
||||
"name": "manager_name",
|
||||
"type": "text",
|
||||
"label": "Manager Name",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required",
|
||||
"placeholder": "Your full name",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Current date will be automatically set",
|
||||
"name": "approval_date",
|
||||
"type": "date",
|
||||
"label": "Approval Date",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required",
|
||||
"placeholder": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"name": "paragraph_approval_footer",
|
||||
"value": "By submitting this form, you confirm that you have reviewed the travel claim and made your decision based on company policy and business necessity.",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
295
docs/json/form/travel-reimbursement-customScript.js
Normal file
295
docs/json/form/travel-reimbursement-customScript.js
Normal file
@ -0,0 +1,295 @@
|
||||
// Travel Reimbursement Form Custom Script Engine
|
||||
// This script provides dynamic form behavior using the FormScriptEngine context
|
||||
|
||||
// Initialize form on load
|
||||
console.log('Travel Reimbursement Form Script Loaded');
|
||||
|
||||
// Auto-calculate total cost when any cost field changes
|
||||
const calculateTotalCost = () => {
|
||||
const transportCost = parseFloat(getField('transport_cost')) || 0;
|
||||
const accommodationCost = parseFloat(getField('accommodation_cost')) || 0;
|
||||
const mealsCost = parseFloat(getField('meals_cost')) || 0;
|
||||
const otherCost = parseFloat(getField('other_cost')) || 0;
|
||||
|
||||
const totalCost = transportCost + accommodationCost + mealsCost + otherCost;
|
||||
|
||||
// Update the total cost display field
|
||||
const totalDisplay = document.querySelector('[data-name="total_cost_display"] .formkit-inner');
|
||||
if (totalDisplay) {
|
||||
totalDisplay.innerHTML = `<strong>RM ${totalCost.toFixed(2)}</strong>`;
|
||||
}
|
||||
|
||||
// Show budget warnings if total is high
|
||||
if (totalCost > 5000) {
|
||||
showInfo('High amount claim - may require manager approval');
|
||||
}
|
||||
|
||||
return totalCost;
|
||||
};
|
||||
|
||||
// Validate email format and domain
|
||||
const validateEmployeeEmail = (email) => {
|
||||
if (!email) return false;
|
||||
|
||||
// Basic email validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
showError('Please enter a valid email address');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Company domain validation (example)
|
||||
const allowedDomains = ['company.com', 'gmail.com', 'outlook.com'];
|
||||
const domain = email.split('@')[1];
|
||||
if (!allowedDomains.includes(domain)) {
|
||||
showInfo('Please use your company email if available');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Validate trip dates
|
||||
const validateTripDates = () => {
|
||||
const departureDate = getField('departure_date');
|
||||
const returnDate = getField('return_date');
|
||||
|
||||
if (!departureDate || !returnDate) return true; // Skip if dates not set
|
||||
|
||||
const departure = new Date(departureDate);
|
||||
const returnDateObj = new Date(returnDate);
|
||||
const today = new Date();
|
||||
|
||||
// Check if return date is after departure date
|
||||
if (returnDateObj <= departure) {
|
||||
showError('Return date must be after departure date');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if trip is too far in the past (more than 3 months)
|
||||
const threeMonthsAgo = new Date();
|
||||
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
|
||||
|
||||
if (departure < threeMonthsAgo) {
|
||||
showError('Cannot claim for trips older than 3 months');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if trip is too far in the future (more than 1 year)
|
||||
const oneYearFromNow = new Date();
|
||||
oneYearFromNow.setFullYear(oneYearFromNow.getFullYear() + 1);
|
||||
|
||||
if (departure > oneYearFromNow) {
|
||||
showError('Cannot claim for trips more than 1 year in advance');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate and show trip duration
|
||||
const diffTime = Math.abs(returnDateObj - departure);
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
|
||||
|
||||
if (diffDays > 30) {
|
||||
showInfo(`Long trip duration: ${diffDays} days - may require additional documentation`);
|
||||
} else {
|
||||
showSuccess(`Trip duration: ${diffDays} days`);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Show/hide fields based on travel type
|
||||
const handleTravelTypeChange = (travelType) => {
|
||||
console.log('Travel type changed to:', travelType);
|
||||
|
||||
// Show different guidance based on travel type
|
||||
switch(travelType) {
|
||||
case 'flight':
|
||||
showInfo('Flight travel: Keep boarding passes and receipts. Maximum transport limit: RM 2,000');
|
||||
break;
|
||||
case 'train':
|
||||
showInfo('Train travel: Keep tickets and receipts. Maximum transport limit: RM 500');
|
||||
break;
|
||||
case 'car':
|
||||
showInfo('Car travel: Keep fuel receipts and toll receipts. Maximum transport limit: RM 800');
|
||||
break;
|
||||
case 'bus':
|
||||
showInfo('Bus travel: Keep tickets and receipts. Maximum transport limit: RM 300');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Validate cost fields for reasonable amounts
|
||||
const validateCostField = (fieldName, value) => {
|
||||
const cost = parseFloat(value) || 0;
|
||||
|
||||
if (cost < 0) {
|
||||
showError(`${fieldName} cannot be negative`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set reasonable limits per field
|
||||
const limits = {
|
||||
'transport_cost': 5000,
|
||||
'accommodation_cost': 10000,
|
||||
'meals_cost': 2000,
|
||||
'other_cost': 1000
|
||||
};
|
||||
|
||||
if (cost > limits[fieldName]) {
|
||||
showError(`${fieldName.replace('_', ' ')} seems unusually high (RM ${cost}). Please verify the amount.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Form validation before submission
|
||||
const validateForm = () => {
|
||||
let isValid = true;
|
||||
const errors = [];
|
||||
|
||||
// Check required fields
|
||||
const requiredFields = {
|
||||
'employee_name': 'Employee Name',
|
||||
'employee_email': 'Employee Email',
|
||||
'department': 'Department',
|
||||
'trip_purpose': 'Trip Purpose',
|
||||
'destination': 'Destination',
|
||||
'departure_date': 'Departure Date',
|
||||
'return_date': 'Return Date',
|
||||
'travel_type': 'Travel Type'
|
||||
};
|
||||
|
||||
Object.entries(requiredFields).forEach(([fieldName, displayName]) => {
|
||||
const value = getField(fieldName);
|
||||
if (!value || value.toString().trim() === '') {
|
||||
errors.push(`${displayName} is required`);
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Validate email
|
||||
const email = getField('employee_email');
|
||||
if (email && !validateEmployeeEmail(email)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate dates
|
||||
if (!validateTripDates()) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Check if at least one cost field has a value > 0
|
||||
const totalCost = calculateTotalCost();
|
||||
if (totalCost <= 0) {
|
||||
errors.push('At least one expense amount must be greater than 0');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Check receipts confirmation
|
||||
const hasReceipts = getField('has_receipts');
|
||||
if (!hasReceipts || hasReceipts.length === 0) {
|
||||
errors.push('Please confirm you have all supporting receipts');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Show validation results
|
||||
if (errors.length > 0) {
|
||||
showError(`Please fix the following errors:\n• ${errors.join('\n• ')}`);
|
||||
} else {
|
||||
showSuccess('Form validation passed! Ready to submit.');
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
// Set up field change handlers
|
||||
onFieldChange('transport_cost', (newValue) => {
|
||||
if (validateCostField('transport_cost', newValue)) {
|
||||
calculateTotalCost();
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('accommodation_cost', (newValue) => {
|
||||
if (validateCostField('accommodation_cost', newValue)) {
|
||||
calculateTotalCost();
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('meals_cost', (newValue) => {
|
||||
if (validateCostField('meals_cost', newValue)) {
|
||||
calculateTotalCost();
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('other_cost', (newValue) => {
|
||||
if (validateCostField('other_cost', newValue)) {
|
||||
calculateTotalCost();
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('employee_email', (newValue) => {
|
||||
if (newValue && newValue.trim() !== '') {
|
||||
validateEmployeeEmail(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
onFieldChange('departure_date', () => {
|
||||
setTimeout(validateTripDates, 100); // Small delay to ensure both dates are updated
|
||||
});
|
||||
|
||||
onFieldChange('return_date', () => {
|
||||
setTimeout(validateTripDates, 100);
|
||||
});
|
||||
|
||||
onFieldChange('travel_type', (newValue) => {
|
||||
if (newValue) {
|
||||
handleTravelTypeChange(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-format employee name (title case)
|
||||
onFieldChange('employee_name', (newValue) => {
|
||||
if (newValue && typeof newValue === 'string') {
|
||||
const formatted = newValue
|
||||
.toLowerCase()
|
||||
.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
|
||||
if (formatted !== newValue) {
|
||||
setTimeout(() => {
|
||||
setField('employee_name', formatted);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Department suggestions
|
||||
onFieldChange('department', (newValue) => {
|
||||
if (newValue && newValue.length >= 2) {
|
||||
const commonDepartments = [
|
||||
'Finance', 'IT', 'Marketing', 'Sales', 'HR', 'Operations',
|
||||
'Engineering', 'Customer Service', 'Legal', 'Procurement'
|
||||
];
|
||||
|
||||
const suggestions = commonDepartments.filter(dept =>
|
||||
dept.toLowerCase().includes(newValue.toLowerCase())
|
||||
);
|
||||
|
||||
if (suggestions.length > 0 && !suggestions.includes(newValue)) {
|
||||
showInfo(`Department suggestions: ${suggestions.join(', ')}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize total cost display on load
|
||||
setTimeout(() => {
|
||||
calculateTotalCost();
|
||||
}, 1000);
|
||||
|
||||
// Add form submission validation
|
||||
// Note: This would typically be connected to the form's submit event
|
||||
// For now, we'll expose it as a global function for manual validation
|
||||
window.validateTravelForm = validateForm;
|
||||
|
||||
console.log('Travel Reimbursement Form Script initialized successfully');
|
384
docs/json/form/travel-reimbursement-form.json
Normal file
384
docs/json/form/travel-reimbursement-form.json
Normal file
@ -0,0 +1,384 @@
|
||||
[
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_travel_claim",
|
||||
"level": 2,
|
||||
"value": "EMPLOYEE TRAVEL REIMBURSEMENT CLAIM",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"name": "paragraph_travel_instructions",
|
||||
"value": "Please fill out all required fields to submit your travel reimbursement claim. Ensure all expenses are supported by receipts.",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_employee_details",
|
||||
"level": 3,
|
||||
"value": "Employee Details",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter your full name as per company records",
|
||||
"name": "employee_name",
|
||||
"type": "text",
|
||||
"label": "Employee Name",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required",
|
||||
"placeholder": "Enter your full name",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter your company email address",
|
||||
"name": "employee_email",
|
||||
"type": "email",
|
||||
"label": "Employee Email",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required|email",
|
||||
"placeholder": "your.email@company.com",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter your department name",
|
||||
"name": "department",
|
||||
"type": "text",
|
||||
"label": "Department",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"placeholder": "e.g., Finance, IT, Marketing",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_trip_details",
|
||||
"level": 3,
|
||||
"value": "Trip Details",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Describe the purpose or reason for this business trip",
|
||||
"name": "trip_purpose",
|
||||
"type": "text",
|
||||
"label": "Trip Purpose",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"placeholder": "e.g., Client meeting, Conference, Training",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter the destination city and country",
|
||||
"name": "destination",
|
||||
"type": "text",
|
||||
"label": "Destination",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required",
|
||||
"placeholder": "e.g., Singapore, Malaysia",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "date",
|
||||
"props": {
|
||||
"help": "Select your departure date",
|
||||
"name": "departure_date",
|
||||
"type": "date",
|
||||
"label": "Departure Date",
|
||||
"width": "25%",
|
||||
"gridColumn": "span 3",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "date",
|
||||
"props": {
|
||||
"help": "Select your return date",
|
||||
"name": "return_date",
|
||||
"type": "date",
|
||||
"label": "Return Date",
|
||||
"width": "25%",
|
||||
"gridColumn": "span 3",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "radio",
|
||||
"props": {
|
||||
"help": "Select your primary mode of transportation",
|
||||
"name": "travel_type",
|
||||
"type": "radio",
|
||||
"label": "Travel Type",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Flight", "value": "flight" },
|
||||
{ "label": "Train", "value": "train" },
|
||||
{ "label": "Car/Taxi", "value": "car" },
|
||||
{ "label": "Bus", "value": "bus" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_cost_breakdown",
|
||||
"level": 3,
|
||||
"value": "Cost Breakdown",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter transportation costs (flights, trains, taxis, etc.)",
|
||||
"name": "transport_cost",
|
||||
"type": "number",
|
||||
"label": "Transportation Cost (RM)",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required|min:0",
|
||||
"placeholder": "0.00",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter accommodation costs (hotels, lodging, etc.)",
|
||||
"name": "accommodation_cost",
|
||||
"type": "number",
|
||||
"label": "Accommodation Cost (RM)",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required|min:0",
|
||||
"placeholder": "0.00",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter meal and food expenses",
|
||||
"name": "meals_cost",
|
||||
"type": "number",
|
||||
"label": "Meals Cost (RM)",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required|min:0",
|
||||
"placeholder": "0.00",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"help": "Enter other miscellaneous expenses",
|
||||
"name": "other_cost",
|
||||
"type": "number",
|
||||
"label": "Other Expenses (RM)",
|
||||
"width": "50%",
|
||||
"gridColumn": "span 6",
|
||||
"validation": "required|min:0",
|
||||
"placeholder": "0.00",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"props": {
|
||||
"name": "total_cost_display",
|
||||
"type": "text",
|
||||
"label": "Total Claim Amount (RM)",
|
||||
"value": "0.00",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"help": "This will be automatically calculated from the above costs",
|
||||
"placeholder": "0.00",
|
||||
"validation": "",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "heading",
|
||||
"props": {
|
||||
"name": "heading_supporting_documents",
|
||||
"level": 3,
|
||||
"value": "Supporting Documents",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"name": "paragraph_document_note",
|
||||
"value": "Please upload all receipts and supporting documents. For this demo, you can skip file uploads.",
|
||||
"width": "100%",
|
||||
"gridColumn": "span 12",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "checkbox",
|
||||
"props": {
|
||||
"help": "Check this box to confirm you have all supporting receipts",
|
||||
"name": "has_receipts",
|
||||
"type": "checkbox",
|
||||
"label": "I have all supporting receipts and documents",
|
||||
"width": "100%",
|
||||
"options": [
|
||||
{ "label": "Yes, I have all required documents", "value": "true" }
|
||||
],
|
||||
"gridColumn": "span 12",
|
||||
"validation": "required",
|
||||
"conditionalLogic": {
|
||||
"action": "show",
|
||||
"enabled": false,
|
||||
"operator": "and",
|
||||
"conditions": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
545
docs/json/process-builder/travel-workflow-process.json
Normal file
545
docs/json/process-builder/travel-workflow-process.json
Normal file
@ -0,0 +1,545 @@
|
||||
{
|
||||
"edges": [
|
||||
{
|
||||
"id": "start-1753000001-form-1753000002-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "start-1753000001",
|
||||
"target": "form-1753000002",
|
||||
"animated": true,
|
||||
"sourceHandle": "start-1753000001-right",
|
||||
"targetHandle": "form-1753000002-left"
|
||||
},
|
||||
{
|
||||
"id": "form-1753000002-script-1753000003-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "form-1753000002",
|
||||
"target": "script-1753000003",
|
||||
"animated": true,
|
||||
"sourceHandle": "form-1753000002-right",
|
||||
"targetHandle": "script-1753000003-left"
|
||||
},
|
||||
{
|
||||
"id": "script-1753000003-api-1753000004-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "script-1753000003",
|
||||
"target": "api-1753000004",
|
||||
"animated": true,
|
||||
"sourceHandle": "script-1753000003-right",
|
||||
"targetHandle": "api-1753000004-left"
|
||||
},
|
||||
{
|
||||
"id": "api-1753000004-script-1753000005-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "api-1753000004",
|
||||
"target": "script-1753000005",
|
||||
"animated": true,
|
||||
"sourceHandle": "api-1753000004-right",
|
||||
"targetHandle": "script-1753000005-left"
|
||||
},
|
||||
{
|
||||
"id": "script-1753000005-gateway-1753000006-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "script-1753000005",
|
||||
"target": "gateway-1753000006",
|
||||
"animated": true,
|
||||
"sourceHandle": "script-1753000005-right",
|
||||
"targetHandle": "gateway-1753000006-left"
|
||||
},
|
||||
{
|
||||
"id": "gateway-1753000006-form-1753000007-edge",
|
||||
"data": { "condition": "isOverBudget === true" },
|
||||
"type": "custom",
|
||||
"label": "Requires Approval",
|
||||
"source": "gateway-1753000006",
|
||||
"target": "form-1753000007",
|
||||
"animated": true,
|
||||
"sourceHandle": "gateway-1753000006-top",
|
||||
"targetHandle": "form-1753000007-left"
|
||||
},
|
||||
{
|
||||
"id": "gateway-1753000006-script-1753000008-edge",
|
||||
"data": { "condition": "isOverBudget === false" },
|
||||
"type": "custom",
|
||||
"label": "Auto Approve",
|
||||
"source": "gateway-1753000006",
|
||||
"target": "script-1753000008",
|
||||
"animated": true,
|
||||
"sourceHandle": "gateway-1753000006-bottom",
|
||||
"targetHandle": "script-1753000008-left"
|
||||
},
|
||||
{
|
||||
"id": "form-1753000007-script-1753000009-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "form-1753000007",
|
||||
"target": "script-1753000009",
|
||||
"animated": true,
|
||||
"sourceHandle": "form-1753000007-right",
|
||||
"targetHandle": "script-1753000009-left"
|
||||
},
|
||||
{
|
||||
"id": "script-1753000008-notification-1753000010-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "script-1753000008",
|
||||
"target": "notification-1753000010",
|
||||
"animated": true,
|
||||
"sourceHandle": "script-1753000008-right",
|
||||
"targetHandle": "notification-1753000010-left"
|
||||
},
|
||||
{
|
||||
"id": "script-1753000009-notification-1753000011-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "script-1753000009",
|
||||
"target": "notification-1753000011",
|
||||
"animated": true,
|
||||
"sourceHandle": "script-1753000009-right",
|
||||
"targetHandle": "notification-1753000011-left"
|
||||
},
|
||||
{
|
||||
"id": "notification-1753000010-end-1753000012-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "notification-1753000010",
|
||||
"target": "end-1753000012",
|
||||
"animated": true,
|
||||
"sourceHandle": "notification-1753000010-right",
|
||||
"targetHandle": "end-1753000012-left"
|
||||
},
|
||||
{
|
||||
"id": "notification-1753000011-end-1753000012-edge",
|
||||
"data": {},
|
||||
"type": "custom",
|
||||
"label": "",
|
||||
"source": "notification-1753000011",
|
||||
"target": "end-1753000012",
|
||||
"animated": true,
|
||||
"sourceHandle": "notification-1753000011-bottom",
|
||||
"targetHandle": "end-1753000012-top"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start-1753000001",
|
||||
"data": {
|
||||
"label": "Start Travel Claim",
|
||||
"description": "Employee initiates travel reimbursement claim",
|
||||
"shape": "circle",
|
||||
"backgroundColor": "#dcfce7",
|
||||
"borderColor": "#10b981"
|
||||
},
|
||||
"type": "start",
|
||||
"label": "Start Travel Claim",
|
||||
"position": { "x": 100, "y": 300 }
|
||||
},
|
||||
{
|
||||
"id": "form-1753000002",
|
||||
"data": {
|
||||
"label": "Travel Claim Form",
|
||||
"shape": "rectangle",
|
||||
"formId": 2,
|
||||
"formName": "Employee Travel Reimbursement Claim",
|
||||
"formUuid": "travel-reimbursement-form-uuid",
|
||||
"textColor": "#6b21a8",
|
||||
"borderColor": "#9333ea",
|
||||
"description": "Employee fills travel reimbursement claim form",
|
||||
"assignedRoles": [
|
||||
{ "label": "Employee", "value": "3", "description": "Regular employee role" }
|
||||
],
|
||||
"assignedUsers": [],
|
||||
"inputMappings": [],
|
||||
"assignmentType": "roles",
|
||||
"outputMappings": [
|
||||
{ "formField": "employee_name", "processVariable": "employeeName" },
|
||||
{ "formField": "employee_email", "processVariable": "employeeEmail" },
|
||||
{ "formField": "department", "processVariable": "department" },
|
||||
{ "formField": "trip_purpose", "processVariable": "tripPurpose" },
|
||||
{ "formField": "destination", "processVariable": "destination" },
|
||||
{ "formField": "departure_date", "processVariable": "departureDate" },
|
||||
{ "formField": "return_date", "processVariable": "returnDate" },
|
||||
{ "formField": "travel_type", "processVariable": "travelType" },
|
||||
{ "formField": "transport_cost", "processVariable": "transportCost" },
|
||||
{ "formField": "accommodation_cost", "processVariable": "accommodationCost" },
|
||||
{ "formField": "meals_cost", "processVariable": "mealsCost" },
|
||||
{ "formField": "other_cost", "processVariable": "otherCost" },
|
||||
{ "formField": "has_receipts", "processVariable": "hasReceipts" }
|
||||
],
|
||||
"backgroundColor": "#faf5ff",
|
||||
"fieldConditions": [],
|
||||
"assignmentVariable": "",
|
||||
"assignmentVariableType": "user_id"
|
||||
},
|
||||
"type": "form",
|
||||
"label": "Travel Claim Form",
|
||||
"position": { "x": 400, "y": 270 }
|
||||
},
|
||||
{
|
||||
"id": "script-1753000003",
|
||||
"data": {
|
||||
"label": "Validate & Calculate Total",
|
||||
"shape": "rectangle",
|
||||
"textColor": "#374151",
|
||||
"scriptCode": "// Validate form inputs\nconst transport = parseFloat(processVariables.transportCost) || 0;\nconst accommodation = parseFloat(processVariables.accommodationCost) || 0;\nconst meals = parseFloat(processVariables.mealsCost) || 0;\nconst other = parseFloat(processVariables.otherCost) || 0;\n\n// Calculate total cost\nprocessVariables.totalCost = transport + accommodation + meals + other;\n\n// Validate required fields\nif (!processVariables.employeeName || !processVariables.tripPurpose || !processVariables.destination) {\n processVariables.validationError = 'Required fields are missing';\n processVariables.isValidSubmission = false;\n} else if (processVariables.totalCost <= 0) {\n processVariables.validationError = 'Total cost must be greater than 0';\n processVariables.isValidSubmission = false;\n} else {\n processVariables.validationError = '';\n processVariables.isValidSubmission = true;\n}\n\n// Calculate trip duration\nconst departure = new Date(processVariables.departureDate);\nconst returnDate = new Date(processVariables.returnDate);\nprocessVariables.tripDuration = Math.ceil((returnDate - departure) / (1000 * 60 * 60 * 24)) + 1;\n\n// Create submission timestamp\nprocessVariables.submissionTimestamp = new Date().toISOString();",
|
||||
"borderColor": "#6b7280",
|
||||
"description": "Validate form data and calculate totals",
|
||||
"inputVariables": [
|
||||
"employeeName", "tripPurpose", "destination", "transportCost",
|
||||
"accommodationCost", "mealsCost", "otherCost", "departureDate", "returnDate"
|
||||
],
|
||||
"scriptLanguage": "javascript",
|
||||
"backgroundColor": "#f9fafb",
|
||||
"outputVariables": [
|
||||
{
|
||||
"name": "totalCost",
|
||||
"type": "number",
|
||||
"description": "Total claim amount"
|
||||
},
|
||||
{
|
||||
"name": "isValidSubmission",
|
||||
"type": "boolean",
|
||||
"description": "Whether submission is valid"
|
||||
},
|
||||
{
|
||||
"name": "validationError",
|
||||
"type": "string",
|
||||
"description": "Validation error message if any"
|
||||
},
|
||||
{
|
||||
"name": "tripDuration",
|
||||
"type": "number",
|
||||
"description": "Trip duration in days"
|
||||
},
|
||||
{
|
||||
"name": "submissionTimestamp",
|
||||
"type": "string",
|
||||
"description": "When claim was submitted"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "script",
|
||||
"label": "Validate & Calculate Total",
|
||||
"position": { "x": 750, "y": 270 }
|
||||
},
|
||||
{
|
||||
"id": "api-1753000004",
|
||||
"data": {
|
||||
"body": {
|
||||
"data": "{ \"travel_type\": \"{travelType}\", \"destination\": \"{destination}\", \"trip_duration\": {tripDuration} }",
|
||||
"type": "raw"
|
||||
},
|
||||
"label": "Get Policy Rates",
|
||||
"shape": "rectangle",
|
||||
"apiUrl": "https://jsonplaceholder.typicode.com/posts",
|
||||
"params": [],
|
||||
"headers": [{ "key": "Content-Type", "value": "application/json" }],
|
||||
"apiMethod": "POST",
|
||||
"textColor": "#1e40af",
|
||||
"borderColor": "#3b82f6",
|
||||
"description": "Get company policy rates for travel type and destination",
|
||||
"requestBody": "",
|
||||
"authorization": { "type": "none" },
|
||||
"errorVariable": "policyApiError",
|
||||
"outputVariable": "policyApiResponse",
|
||||
"backgroundColor": "#eff6ff",
|
||||
"continueOnError": false
|
||||
},
|
||||
"type": "api",
|
||||
"label": "Get Policy Rates",
|
||||
"position": { "x": 1100, "y": 270 }
|
||||
},
|
||||
{
|
||||
"id": "script-1753000005",
|
||||
"data": {
|
||||
"label": "Calculate Reimbursement",
|
||||
"shape": "rectangle",
|
||||
"textColor": "#374151",
|
||||
"scriptCode": "// Process policy API response\nconst policyResponse = processVariables.policyApiResponse || {};\n\n// Mock policy rates based on travel type (in real system, would come from API)\nlet maxTransport = 0, maxAccommodation = 0, maxMeals = 0, maxOther = 0;\n\nswitch(processVariables.travelType) {\n case 'flight':\n maxTransport = 2000;\n maxAccommodation = 300 * processVariables.tripDuration;\n maxMeals = 150 * processVariables.tripDuration;\n maxOther = 100 * processVariables.tripDuration;\n break;\n case 'train':\n maxTransport = 500;\n maxAccommodation = 250 * processVariables.tripDuration;\n maxMeals = 120 * processVariables.tripDuration;\n maxOther = 80 * processVariables.tripDuration;\n break;\n case 'car':\n maxTransport = 800;\n maxAccommodation = 200 * processVariables.tripDuration;\n maxMeals = 100 * processVariables.tripDuration;\n maxOther = 60 * processVariables.tripDuration;\n break;\n default:\n maxTransport = 300;\n maxAccommodation = 150 * processVariables.tripDuration;\n maxMeals = 80 * processVariables.tripDuration;\n maxOther = 50 * processVariables.tripDuration;\n}\n\n// Calculate allowed amounts\nprocessVariables.allowedTransport = Math.min(processVariables.transportCost, maxTransport);\nprocessVariables.allowedAccommodation = Math.min(processVariables.accommodationCost, maxAccommodation);\nprocessVariables.allowedMeals = Math.min(processVariables.mealsCost, maxMeals);\nprocessVariables.allowedOther = Math.min(processVariables.otherCost, maxOther);\n\n// Calculate total allowed and reimbursement\nprocessVariables.totalAllowed = processVariables.allowedTransport + processVariables.allowedAccommodation + processVariables.allowedMeals + processVariables.allowedOther;\nprocessVariables.reimbursementAmount = processVariables.totalAllowed;\n\n// Check if over budget\nprocessVariables.isOverBudget = processVariables.totalCost > processVariables.totalAllowed;\nprocessVariables.overBudgetAmount = Math.max(0, processVariables.totalCost - processVariables.totalAllowed);\n\n// Create claim summary\nprocessVariables.claimSummary = `Travel Claim - ${processVariables.employeeName} - ${processVariables.tripPurpose} - ${processVariables.destination} - RM${processVariables.reimbursementAmount.toFixed(2)} reimbursement`;\n\n// Set initial approval status\nprocessVariables.approvalStatus = processVariables.isOverBudget ? 'pending_approval' : 'auto_approved';",
|
||||
"borderColor": "#6b7280",
|
||||
"description": "Calculate reimbursement based on policy rates",
|
||||
"inputVariables": ["policyApiResponse", "totalCost", "travelType", "tripDuration"],
|
||||
"scriptLanguage": "javascript",
|
||||
"backgroundColor": "#f9fafb",
|
||||
"outputVariables": [
|
||||
{
|
||||
"name": "totalAllowed",
|
||||
"type": "number",
|
||||
"description": "Total allowed reimbursement per policy"
|
||||
},
|
||||
{
|
||||
"name": "reimbursementAmount",
|
||||
"type": "number",
|
||||
"description": "Final reimbursement amount"
|
||||
},
|
||||
{
|
||||
"name": "isOverBudget",
|
||||
"type": "boolean",
|
||||
"description": "Whether claim exceeds policy limits"
|
||||
},
|
||||
{
|
||||
"name": "overBudgetAmount",
|
||||
"type": "number",
|
||||
"description": "Amount over budget"
|
||||
},
|
||||
{
|
||||
"name": "claimSummary",
|
||||
"type": "string",
|
||||
"description": "Summary of the claim"
|
||||
},
|
||||
{
|
||||
"name": "approvalStatus",
|
||||
"type": "string",
|
||||
"description": "Current approval status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "script",
|
||||
"label": "Calculate Reimbursement",
|
||||
"position": { "x": 1450, "y": 270 }
|
||||
},
|
||||
{
|
||||
"id": "gateway-1753000006",
|
||||
"data": {
|
||||
"label": "Budget Check",
|
||||
"shape": "diamond",
|
||||
"textColor": "#c2410c",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-group-1",
|
||||
"output": "Requires Approval",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-over-budget",
|
||||
"value": "true",
|
||||
"maxValue": "",
|
||||
"minValue": "",
|
||||
"operator": "is_true",
|
||||
"variable": "isOverBudget",
|
||||
"valueType": "boolean",
|
||||
"logicalOperator": "and"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "condition-group-2",
|
||||
"output": "Auto Approve",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-within-budget",
|
||||
"value": "false",
|
||||
"maxValue": "",
|
||||
"minValue": "",
|
||||
"operator": "is_false",
|
||||
"variable": "isOverBudget",
|
||||
"valueType": "boolean",
|
||||
"logicalOperator": "and"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"borderColor": "#f97316",
|
||||
"defaultPath": "Auto Approve",
|
||||
"description": "Check if claim exceeds policy limits",
|
||||
"backgroundColor": "#fff7ed"
|
||||
},
|
||||
"type": "gateway",
|
||||
"label": "Budget Check",
|
||||
"position": { "x": 1800, "y": 270 }
|
||||
},
|
||||
{
|
||||
"id": "form-1753000007",
|
||||
"data": {
|
||||
"label": "Manager Approval",
|
||||
"shape": "rectangle",
|
||||
"formId": 3,
|
||||
"formName": "Travel Claim Approval Form",
|
||||
"formUuid": "travel-approval-form-uuid",
|
||||
"textColor": "#dc2626",
|
||||
"borderColor": "#ef4444",
|
||||
"description": "Manager reviews and approves/rejects over-budget travel claim",
|
||||
"assignedRoles": [
|
||||
{ "label": "Manager", "value": "4", "description": "Department manager role" }
|
||||
],
|
||||
"assignedUsers": [],
|
||||
"inputMappings": [
|
||||
{ "processVariable": "claimSummary", "formField": "claim_summary" },
|
||||
{ "processVariable": "totalCost", "formField": "total_cost" },
|
||||
{ "processVariable": "reimbursementAmount", "formField": "reimbursement_amount" },
|
||||
{ "processVariable": "overBudgetAmount", "formField": "over_budget_amount" }
|
||||
],
|
||||
"assignmentType": "roles",
|
||||
"outputMappings": [
|
||||
{ "formField": "manager_decision", "processVariable": "managerDecision" },
|
||||
{ "formField": "manager_comments", "processVariable": "managerComments" }
|
||||
],
|
||||
"backgroundColor": "#fef2f2",
|
||||
"fieldConditions": [],
|
||||
"assignmentVariable": "",
|
||||
"assignmentVariableType": "user_id"
|
||||
},
|
||||
"type": "form",
|
||||
"label": "Manager Approval",
|
||||
"position": { "x": 2150, "y": 100 }
|
||||
},
|
||||
{
|
||||
"id": "script-1753000008",
|
||||
"data": {
|
||||
"label": "Auto Approve & Store",
|
||||
"shape": "rectangle",
|
||||
"textColor": "#374151",
|
||||
"scriptCode": "// Auto-approve claim within budget\nprocessVariables.finalApprovalStatus = 'approved';\nprocessVariables.approvedAmount = processVariables.reimbursementAmount;\nprocessVariables.approvalTimestamp = new Date().toISOString();\nprocessVariables.approvedBy = 'System (Auto-approved)';\nprocessVariables.approvalComments = 'Claim is within policy limits and auto-approved.';\n\n// Create final claim record for storage\nprocessVariables.finalClaimRecord = {\n employeeName: processVariables.employeeName,\n employeeEmail: processVariables.employeeEmail,\n department: processVariables.department,\n tripPurpose: processVariables.tripPurpose,\n destination: processVariables.destination,\n totalClaimed: processVariables.totalCost,\n approvedAmount: processVariables.approvedAmount,\n status: processVariables.finalApprovalStatus,\n submissionDate: processVariables.submissionTimestamp,\n approvalDate: processVariables.approvalTimestamp,\n approvedBy: processVariables.approvedBy\n};\n\n// Mock API call to store claim (in real system, would call actual storage API)\nprocessVariables.claimId = 'CLAIM-' + Date.now();\nprocessVariables.storageResult = 'success';",
|
||||
"borderColor": "#10b981",
|
||||
"description": "Auto-approve claim and store in system",
|
||||
"inputVariables": ["reimbursementAmount", "claimSummary"],
|
||||
"scriptLanguage": "javascript",
|
||||
"backgroundColor": "#f0fdf4",
|
||||
"outputVariables": [
|
||||
{
|
||||
"name": "finalApprovalStatus",
|
||||
"type": "string",
|
||||
"description": "Final approval status"
|
||||
},
|
||||
{
|
||||
"name": "approvedAmount",
|
||||
"type": "number",
|
||||
"description": "Final approved amount"
|
||||
},
|
||||
{
|
||||
"name": "claimId",
|
||||
"type": "string",
|
||||
"description": "Generated claim ID"
|
||||
},
|
||||
{
|
||||
"name": "approvalTimestamp",
|
||||
"type": "string",
|
||||
"description": "When claim was approved"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "script",
|
||||
"label": "Auto Approve & Store",
|
||||
"position": { "x": 2150, "y": 440 }
|
||||
},
|
||||
{
|
||||
"id": "script-1753000009",
|
||||
"data": {
|
||||
"label": "Process Manager Decision",
|
||||
"shape": "rectangle",
|
||||
"textColor": "#374151",
|
||||
"scriptCode": "// Process manager's decision\nprocessVariables.finalApprovalStatus = processVariables.managerDecision === 'approve' ? 'approved' : 'rejected';\nprocessVariables.approvalTimestamp = new Date().toISOString();\nprocessVariables.approvedBy = 'Manager';\nprocessVariables.approvalComments = processVariables.managerComments || '';\n\n// Set approved amount based on decision\nif (processVariables.finalApprovalStatus === 'approved') {\n processVariables.approvedAmount = processVariables.totalCost; // Manager can approve full amount\n} else {\n processVariables.approvedAmount = 0;\n}\n\n// Create final claim record for storage\nprocessVariables.finalClaimRecord = {\n employeeName: processVariables.employeeName,\n employeeEmail: processVariables.employeeEmail,\n department: processVariables.department,\n tripPurpose: processVariables.tripPurpose,\n destination: processVariables.destination,\n totalClaimed: processVariables.totalCost,\n approvedAmount: processVariables.approvedAmount,\n status: processVariables.finalApprovalStatus,\n submissionDate: processVariables.submissionTimestamp,\n approvalDate: processVariables.approvalTimestamp,\n approvedBy: processVariables.approvedBy,\n managerComments: processVariables.approvalComments\n};\n\n// Mock API call to store claim\nprocessVariables.claimId = 'CLAIM-' + Date.now();\nprocessVariables.storageResult = 'success';",
|
||||
"borderColor": "#6b7280",
|
||||
"description": "Process manager decision and store final result",
|
||||
"inputVariables": ["managerDecision", "managerComments", "totalCost"],
|
||||
"scriptLanguage": "javascript",
|
||||
"backgroundColor": "#f9fafb",
|
||||
"outputVariables": [
|
||||
{
|
||||
"name": "finalApprovalStatus",
|
||||
"type": "string",
|
||||
"description": "Final approval status after manager review"
|
||||
},
|
||||
{
|
||||
"name": "approvedAmount",
|
||||
"type": "number",
|
||||
"description": "Final approved amount"
|
||||
},
|
||||
{
|
||||
"name": "claimId",
|
||||
"type": "string",
|
||||
"description": "Generated claim ID"
|
||||
},
|
||||
{
|
||||
"name": "approvalTimestamp",
|
||||
"type": "string",
|
||||
"description": "When claim was processed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "script",
|
||||
"label": "Process Manager Decision",
|
||||
"position": { "x": 2500, "y": 100 }
|
||||
},
|
||||
{
|
||||
"id": "notification-1753000010",
|
||||
"data": {
|
||||
"label": "Auto Approval Notification",
|
||||
"message": "Your travel claim has been auto-approved",
|
||||
"subject": "Travel Claim Auto-Approved - {claimId}",
|
||||
"priority": "medium",
|
||||
"expiration": { "unit": "hours", "value": 24, "enabled": false },
|
||||
"description": "Notify employee of auto-approved claim",
|
||||
"htmlMessage": "<div style=\"font-family: Arial, sans-serif; padding: 20px; border-left: 4px solid #10b981;\">\n <h2 style=\"color: #10b981; margin-top: 0;\">✅ Travel Claim Auto-Approved</h2>\n <p>Dear {employeeName},</p>\n <p>Your travel reimbursement claim has been <strong>automatically approved</strong>!</p>\n \n <div style=\"background: #f0fdf4; padding: 15px; border-radius: 8px; margin: 20px 0;\">\n <h3 style=\"margin-top: 0; color: #059669;\">Claim Details:</h3>\n <ul style=\"list-style: none; padding: 0;\">\n <li><strong>Claim ID:</strong> {claimId}</li>\n <li><strong>Trip Purpose:</strong> {tripPurpose}</li>\n <li><strong>Destination:</strong> {destination}</li>\n <li><strong>Total Claimed:</strong> RM{totalCost}</li>\n <li><strong>Approved Amount:</strong> RM{approvedAmount}</li>\n <li><strong>Status:</strong> {finalApprovalStatus}</li>\n </ul>\n </div>\n \n <p>Your reimbursement will be processed within 3-5 business days.</p>\n \n <p style=\"margin-bottom: 0;\">Best regards,<br>\n <strong>Travel Management System</strong></p>\n</div>",
|
||||
"messageFormat": "html",
|
||||
"recipientRole": "",
|
||||
"recipientType": "email",
|
||||
"recipientUser": "",
|
||||
"recipientEmail": "{employeeEmail}",
|
||||
"recipientGroup": "",
|
||||
"deliveryOptions": { "sms": false, "email": true, "inApp": true },
|
||||
"richTextMessage": "",
|
||||
"notificationType": "success",
|
||||
"recipientVariable": "employeeEmail"
|
||||
},
|
||||
"type": "notification",
|
||||
"label": "Auto Approval Notification",
|
||||
"position": { "x": 2500, "y": 440 }
|
||||
},
|
||||
{
|
||||
"id": "notification-1753000011",
|
||||
"data": {
|
||||
"label": "Manager Decision Notification",
|
||||
"message": "Your travel claim has been reviewed by your manager",
|
||||
"subject": "Travel Claim Decision - {claimId}",
|
||||
"priority": "high",
|
||||
"expiration": { "unit": "hours", "value": 48, "enabled": false },
|
||||
"description": "Notify employee of manager's decision",
|
||||
"htmlMessage": "<div style=\"font-family: Arial, sans-serif; padding: 20px; border-left: 4px solid #3b82f6;\">\n <h2 style=\"color: #1e40af; margin-top: 0;\">📋 Travel Claim Decision</h2>\n <p>Dear {employeeName},</p>\n <p>Your manager has reviewed your travel reimbursement claim.</p>\n \n <div style=\"background: #eff6ff; padding: 15px; border-radius: 8px; margin: 20px 0;\">\n <h3 style=\"margin-top: 0; color: #1e40af;\">Claim Details:</h3>\n <ul style=\"list-style: none; padding: 0;\">\n <li><strong>Claim ID:</strong> {claimId}</li>\n <li><strong>Trip Purpose:</strong> {tripPurpose}</li>\n <li><strong>Destination:</strong> {destination}</li>\n <li><strong>Total Claimed:</strong> RM{totalCost}</li>\n <li><strong>Approved Amount:</strong> RM{approvedAmount}</li>\n <li><strong>Status:</strong> <span style=\"font-weight: bold; color: {finalApprovalStatus === 'approved' ? '#059669' : '#dc2626'};\">{finalApprovalStatus.toUpperCase()}</span></li>\n </ul>\n </div>\n \n <div style=\"background: #f8fafc; padding: 15px; border-radius: 8px; margin: 20px 0;\">\n <h4 style=\"margin-top: 0;\">Manager Comments:</h4>\n <p style=\"font-style: italic;\">{approvalComments}</p>\n </div>\n \n <p>If approved, your reimbursement will be processed within 3-5 business days.</p>\n \n <p style=\"margin-bottom: 0;\">Best regards,<br>\n <strong>Travel Management System</strong></p>\n</div>",
|
||||
"messageFormat": "html",
|
||||
"recipientRole": "",
|
||||
"recipientType": "email",
|
||||
"recipientUser": "",
|
||||
"recipientEmail": "{employeeEmail}",
|
||||
"recipientGroup": "",
|
||||
"deliveryOptions": { "sms": false, "email": true, "inApp": true },
|
||||
"richTextMessage": "",
|
||||
"notificationType": "info",
|
||||
"recipientVariable": "employeeEmail"
|
||||
},
|
||||
"type": "notification",
|
||||
"label": "Manager Decision Notification",
|
||||
"position": { "x": 2850, "y": 100 }
|
||||
},
|
||||
{
|
||||
"id": "end-1753000012",
|
||||
"data": {
|
||||
"label": "End",
|
||||
"description": "Travel claim process completed",
|
||||
"shape": "circle",
|
||||
"backgroundColor": "#fef3c7",
|
||||
"borderColor": "#f59e0b"
|
||||
},
|
||||
"type": "end",
|
||||
"label": "End",
|
||||
"position": { "x": 3200, "y": 300 }
|
||||
}
|
||||
],
|
||||
"viewport": {
|
||||
"x": -50,
|
||||
"y": 50,
|
||||
"zoom": 0.6
|
||||
}
|
||||
}
|
282
docs/json/process-builder/travel-workflow-variables.json
Normal file
282
docs/json/process-builder/travel-workflow-variables.json
Normal file
@ -0,0 +1,282 @@
|
||||
{
|
||||
"employeeName": {
|
||||
"name": "employeeName",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Full name of the employee submitting the claim"
|
||||
},
|
||||
"employeeEmail": {
|
||||
"name": "employeeEmail",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Email address of the employee for notifications"
|
||||
},
|
||||
"department": {
|
||||
"name": "department",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Employee's department name"
|
||||
},
|
||||
"tripPurpose": {
|
||||
"name": "tripPurpose",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Purpose or reason for the business trip"
|
||||
},
|
||||
"destination": {
|
||||
"name": "destination",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Travel destination city and country"
|
||||
},
|
||||
"departureDate": {
|
||||
"name": "departureDate",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Trip departure date in ISO format"
|
||||
},
|
||||
"returnDate": {
|
||||
"name": "returnDate",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Trip return date in ISO format"
|
||||
},
|
||||
"travelType": {
|
||||
"name": "travelType",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Primary mode of transportation (flight, train, car, bus)"
|
||||
},
|
||||
"transportCost": {
|
||||
"name": "transportCost",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Transportation expenses claimed"
|
||||
},
|
||||
"accommodationCost": {
|
||||
"name": "accommodationCost",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Accommodation expenses claimed"
|
||||
},
|
||||
"mealsCost": {
|
||||
"name": "mealsCost",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Meals and food expenses claimed"
|
||||
},
|
||||
"otherCost": {
|
||||
"name": "otherCost",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Other miscellaneous expenses claimed"
|
||||
},
|
||||
"hasReceipts": {
|
||||
"name": "hasReceipts",
|
||||
"type": "boolean",
|
||||
"scope": "global",
|
||||
"value": false,
|
||||
"description": "Whether employee has all supporting receipts"
|
||||
},
|
||||
"totalCost": {
|
||||
"name": "totalCost",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Total amount claimed (calculated)"
|
||||
},
|
||||
"isValidSubmission": {
|
||||
"name": "isValidSubmission",
|
||||
"type": "boolean",
|
||||
"scope": "global",
|
||||
"value": false,
|
||||
"description": "Whether the form submission passed validation"
|
||||
},
|
||||
"validationError": {
|
||||
"name": "validationError",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Validation error message if submission is invalid"
|
||||
},
|
||||
"tripDuration": {
|
||||
"name": "tripDuration",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Trip duration in days (calculated)"
|
||||
},
|
||||
"submissionTimestamp": {
|
||||
"name": "submissionTimestamp",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "ISO timestamp when claim was submitted"
|
||||
},
|
||||
"policyApiResponse": {
|
||||
"name": "policyApiResponse",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Response from company policy rates API"
|
||||
},
|
||||
"policyApiError": {
|
||||
"name": "policyApiError",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Error from policy API if request fails"
|
||||
},
|
||||
"allowedTransport": {
|
||||
"name": "allowedTransport",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Maximum allowed transportation reimbursement per policy"
|
||||
},
|
||||
"allowedAccommodation": {
|
||||
"name": "allowedAccommodation",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Maximum allowed accommodation reimbursement per policy"
|
||||
},
|
||||
"allowedMeals": {
|
||||
"name": "allowedMeals",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Maximum allowed meals reimbursement per policy"
|
||||
},
|
||||
"allowedOther": {
|
||||
"name": "allowedOther",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Maximum allowed other expenses reimbursement per policy"
|
||||
},
|
||||
"totalAllowed": {
|
||||
"name": "totalAllowed",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Total allowed reimbursement according to company policy"
|
||||
},
|
||||
"reimbursementAmount": {
|
||||
"name": "reimbursementAmount",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Final calculated reimbursement amount"
|
||||
},
|
||||
"isOverBudget": {
|
||||
"name": "isOverBudget",
|
||||
"type": "boolean",
|
||||
"scope": "global",
|
||||
"value": false,
|
||||
"description": "Whether claimed amount exceeds policy limits"
|
||||
},
|
||||
"overBudgetAmount": {
|
||||
"name": "overBudgetAmount",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Amount by which claim exceeds policy limits"
|
||||
},
|
||||
"claimSummary": {
|
||||
"name": "claimSummary",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Formatted summary string of the travel claim"
|
||||
},
|
||||
"approvalStatus": {
|
||||
"name": "approvalStatus",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "pending",
|
||||
"description": "Current approval status (pending_approval, auto_approved, etc.)"
|
||||
},
|
||||
"managerDecision": {
|
||||
"name": "managerDecision",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Manager's approval decision (approve/reject)"
|
||||
},
|
||||
"managerComments": {
|
||||
"name": "managerComments",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Manager's comments on the approval decision"
|
||||
},
|
||||
"finalApprovalStatus": {
|
||||
"name": "finalApprovalStatus",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Final approval status after all reviews (approved/rejected)"
|
||||
},
|
||||
"approvedAmount": {
|
||||
"name": "approvedAmount",
|
||||
"type": "number",
|
||||
"scope": "global",
|
||||
"value": 0,
|
||||
"description": "Final approved reimbursement amount"
|
||||
},
|
||||
"approvalTimestamp": {
|
||||
"name": "approvalTimestamp",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "ISO timestamp when claim was approved/rejected"
|
||||
},
|
||||
"approvedBy": {
|
||||
"name": "approvedBy",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Who approved the claim (System/Manager name)"
|
||||
},
|
||||
"approvalComments": {
|
||||
"name": "approvalComments",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Final approval comments"
|
||||
},
|
||||
"finalClaimRecord": {
|
||||
"name": "finalClaimRecord",
|
||||
"type": "object",
|
||||
"scope": "global",
|
||||
"value": null,
|
||||
"description": "Complete claim record object for storage/archival"
|
||||
},
|
||||
"claimId": {
|
||||
"name": "claimId",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Unique identifier generated for the claim"
|
||||
},
|
||||
"storageResult": {
|
||||
"name": "storageResult",
|
||||
"type": "string",
|
||||
"scope": "global",
|
||||
"value": "",
|
||||
"description": "Result of storing claim in database (success/error)"
|
||||
}
|
||||
}
|
@ -1240,7 +1240,7 @@ function getConditionGroupResult(conditionGroup, variables) {
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="container mx-auto px-6 py-8 max-w-4xl">
|
||||
<div class="container mx-auto px-6 py-8 max-w-[1200px]">
|
||||
<!-- Loading State -->
|
||||
<div v-if="loading" class="flex justify-center items-center py-12">
|
||||
<div class="text-center">
|
||||
@ -1353,7 +1353,7 @@ function getConditionGroupResult(conditionGroup, variables) {
|
||||
:incomplete-message="false"
|
||||
validation-visibility="submit"
|
||||
>
|
||||
<div class="space-y-6">
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<template v-for="(component, index) in currentForm.formComponents" :key="index">
|
||||
<ComponentPreview
|
||||
:component="component"
|
||||
|
Loading…
x
Reference in New Issue
Block a user