corrad-bp/docs/json/form/manager-approval-customScript.js
Md Afiq Iskandar b1fc3d027a 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.
2025-07-25 12:02:13 +08:00

232 lines
7.2 KiB
JavaScript

// 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');