diff --git a/PAYROLL_BATCH_PROCESSING.md b/PAYROLL_BATCH_PROCESSING.md new file mode 100644 index 0000000..1325e4e --- /dev/null +++ b/PAYROLL_BATCH_PROCESSING.md @@ -0,0 +1,140 @@ +# Payroll Batch Processing & Approval Workflows + +## Overview + +The payroll batch processing system enables efficient processing of multiple worker payrolls simultaneously with built-in multi-step approval workflows. This ensures proper oversight and control over payroll operations while maintaining audit trails. + +## Features + +### 1. Batch Processing +- **Worker Selection**: Select multiple workers with approved timesheets for batch processing +- **Automatic Calculations**: Automatically calculates gross pay, deductions (tax, NI), and net pay for each worker +- **Validation**: Pre-submission validation checks for: + - Invalid amounts + - Missing timesheets + - Excessive hours warnings + - Unusually high payment warnings +- **Batch Summary**: Real-time totals showing workers, timesheets, hours, and amounts + +### 2. Approval Workflow +The system implements a multi-step approval process: + +#### Default Workflow Steps: +1. **Manager Review** - Initial review by line manager +2. **Finance Approval** - Financial oversight and validation +3. **Final Approval** - Executive or admin final sign-off + +#### Workflow Features: +- **Role-Based Approvals**: Each step requires approval from specific user roles +- **Sequential Processing**: Steps must be completed in order +- **Comments/Notes**: Approvers can add comments at each step +- **Rejection Handling**: Any step can reject the batch with mandatory reason +- **Audit Trail**: Full history of approvals, rejections, and comments + +### 3. Batch States +- **Draft**: Initial creation, not yet submitted +- **Validating**: Undergoing validation checks +- **Pending Approval**: Submitted and awaiting approvals +- **Approved**: All approvals complete, ready for processing +- **Rejected**: Rejected at an approval step +- **Processing**: Being processed for payment +- **Completed**: Fully processed and paid + +### 4. User Interface + +#### Batch Processing Tab +- Worker selection with checkboxes +- Batch totals dashboard +- Worker details showing: + - Name and role + - Total hours and timesheet count + - Gross pay amount + - Payment method +- Validate & Process button + +#### Approval Queue Tab +- List of all batches with status filters +- Search functionality +- Batch cards showing: + - Batch ID and creation date + - Period covered + - Worker count and total amount + - Workflow progress indicator + - Status badges + +#### Batch Detail View +- Complete workflow visualization +- Step-by-step approval status +- Approver information and timestamps +- Worker breakdown with deductions +- Batch metadata and audit information + +### 5. Data Persistence +All batch data is stored in IndexedDB for: +- Offline access +- Fast retrieval +- Session persistence +- Local caching + +## Usage + +### Creating a Batch +1. Navigate to Payroll > Batch Processing tab +2. Select workers from the list (those with approved timesheets) +3. Review batch totals +4. Click "Validate & Process" +5. Review validation results +6. Submit for approval + +### Approving a Batch +1. Navigate to Payroll > Approval Queue tab +2. Click on a pending batch +3. Review batch details and workers +4. Click "Approve" or "Reject" at your workflow step +5. Add comments (optional for approve, required for reject) +6. Submit decision + +### Monitoring Batches +- Use status filters to find specific batches +- Search by batch ID or creator +- Click any batch to view full details +- Track workflow progress through step indicators + +## Integration Points + +### With Existing Systems: +- **Timesheets**: Only approved timesheets are available for batch processing +- **Workers**: Worker data including payment methods +- **Payroll Calculations**: Uses existing payroll calculation hooks +- **CRUD Operations**: Integrates with IndexedDB CRUD hooks +- **Notifications**: Can trigger notifications at each workflow step +- **Audit Logs**: All actions are auditable + +## Security & Permissions + +The system respects role-based permissions: +- Only authorized roles can approve at their workflow step +- Rejection reasons are mandatory and audited +- All state changes are logged +- User identity is tracked throughout the workflow + +## Benefits + +1. **Efficiency**: Process multiple workers at once +2. **Control**: Multi-step approvals prevent errors +3. **Transparency**: Clear audit trail of all decisions +4. **Validation**: Pre-emptive checks catch issues early +5. **Flexibility**: Configurable workflow steps +6. **Audit Compliance**: Full history of all actions + +## Future Enhancements + +Potential improvements: +- Configurable workflow templates +- Parallel approval paths +- Auto-approval rules for low-value batches +- Email notifications at each step +- Batch scheduling +- Recurring batch templates +- Integration with payroll providers +- Export to accounting systems diff --git a/src/components/PayrollApprovalWorkflow.tsx b/src/components/PayrollApprovalWorkflow.tsx new file mode 100644 index 0000000..287bcf2 --- /dev/null +++ b/src/components/PayrollApprovalWorkflow.tsx @@ -0,0 +1,369 @@ +import { useState } from 'react' +import { + CheckCircle, + XCircle, + Clock, + ArrowRight, + ChatCircle, + User, + CalendarBlank +} from '@phosphor-icons/react' +import { Button } from '@/components/ui/button' +import { Badge } from '@/components/ui/badge' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogFooter +} from '@/components/ui/dialog' +import { Textarea } from '@/components/ui/textarea' +import { Label } from '@/components/ui/label' +import { Stack } from '@/components/ui/stack' +import { DataList } from '@/components/ui/data-list' +import { Separator } from '@/components/ui/separator' +import { usePayrollBatch, type PayrollBatch, type ApprovalStep } from '@/hooks/use-payroll-batch' +import { toast } from 'sonner' +import { format } from 'date-fns' + +interface PayrollApprovalWorkflowProps { + batch: PayrollBatch + currentUserRole: string + currentUserName: string + onApprove?: (batchId: string) => void + onReject?: (batchId: string) => void +} + +export function PayrollApprovalWorkflow({ + batch, + currentUserRole, + currentUserName, + onApprove, + onReject +}: PayrollApprovalWorkflowProps) { + const [showApproveDialog, setShowApproveDialog] = useState(false) + const [showRejectDialog, setShowRejectDialog] = useState(false) + const [comments, setComments] = useState('') + const [rejectionReason, setRejectionReason] = useState('') + + const { approveBatchStep, rejectBatchStep } = usePayrollBatch() + + const workflow = batch.approvalWorkflow + const currentStep = workflow?.steps[workflow.currentStep] + const canInteract = currentStep?.approverRole === currentUserRole && currentStep?.status === 'pending' + + const handleApprove = async () => { + if (!currentStep) return + + try { + await approveBatchStep(batch.id, currentStep.id, currentUserName, comments) + toast.success('Batch approved successfully') + setShowApproveDialog(false) + setComments('') + onApprove?.(batch.id) + } catch (error) { + toast.error('Failed to approve batch') + } + } + + const handleReject = async () => { + if (!currentStep) return + + if (!rejectionReason.trim()) { + toast.error('Please provide a reason for rejection') + return + } + + try { + await rejectBatchStep(batch.id, currentStep.id, currentUserName, rejectionReason) + toast.success('Batch rejected') + setShowRejectDialog(false) + setRejectionReason('') + onReject?.(batch.id) + } catch (error) { + toast.error('Failed to reject batch') + } + } + + const getStepStatusBadge = (step: ApprovalStep) => { + switch (step.status) { + case 'approved': + return ( + + + Approved + + ) + case 'rejected': + return ( + + + Rejected + + ) + default: + return ( + + + Pending + + ) + } + } + + const getBatchStatusBadge = (status: string) => { + const statusConfig: Record = { + 'draft': { + label: 'Draft', + className: 'bg-muted text-muted-foreground', + icon: null + }, + 'pending-approval': { + label: 'Pending Approval', + className: 'bg-warning/10 text-warning-foreground border-warning/30', + icon: Clock + }, + 'approved': { + label: 'Approved', + className: 'bg-success/10 text-success-foreground border-success/30', + icon: CheckCircle + }, + 'rejected': { + label: 'Rejected', + className: 'bg-destructive/10 text-destructive-foreground border-destructive/30', + icon: XCircle + }, + 'processing': { + label: 'Processing', + className: 'bg-accent/10 text-accent-foreground border-accent/30', + icon: Clock + }, + 'completed': { + label: 'Completed', + className: 'bg-success/10 text-success-foreground border-success/30', + icon: CheckCircle + } + } + + const config = statusConfig[status] || statusConfig.draft + const Icon = config.icon + + return ( + + {Icon && } + {config.label} + + ) + } + + if (!workflow) { + return ( + + +

+ No approval workflow configured for this batch +

+
+
+ ) + } + + return ( + <> + + +
+ Approval Workflow + {getBatchStatusBadge(batch.status)} +
+
+ + +
+ {workflow.steps.map((step, index) => ( +
+
+
+ {index + 1} +
+
+
+
+
{step.name}
+
+ Approver: {step.approverRole} +
+
+ {getStepStatusBadge(step)} +
+ + {step.status === 'approved' && step.approvedBy && ( +
+
+ +
+
+ Approved by: {step.approvedBy} +
+ {step.approvedAt && ( +
+ {format(new Date(step.approvedAt), 'PPpp')} +
+ )} + {step.comments && ( +
+ + {step.comments} +
+ )} +
+
+
+ )} + + {step.status === 'rejected' && step.rejectedBy && ( +
+
+ +
+
+ Rejected by: {step.rejectedBy} +
+ {step.rejectedAt && ( +
+ {format(new Date(step.rejectedAt), 'PPpp')} +
+ )} + {step.comments && ( +
+ + {step.comments} +
+ )} +
+
+
+ )} + + {step.status === 'pending' && index === workflow.currentStep && canInteract && ( +
+ + +
+ )} +
+
+ {index < workflow.steps.length - 1 && ( +
+ )} +
+ ))} +
+ + + +
+
Batch Details
+ +
+ + + + + + + + Approve Payroll Batch + + You are about to approve this payroll batch. This action will move it to the next approval step. + + + +
+
+ +