Shift Management System
Overviewβ
The shift management system is a comprehensive time-tracking solution implemented across the AttuneLogic platform. It supports both mobile and web applications, providing functionality for:
- Clock in/out management
- Break tracking
- Overtime calculation
- Payroll integration
- Location tracking
- Multi-tenant support
Database Schemaβ
Shift Modelβ
The shift model (models/Shift.js) includes:
{
employee: ObjectId, // Reference to Employee model
startTime: Date, // Required, when shift starts
endTime: Date, // When shift ends
totalTime: Number, // Total shift duration in milliseconds
totalBreakTime: Number, // Total break time in milliseconds
totalWorkTime: Number, // Actual working time (totalTime - totalBreakTime)
status: String, // ["scheduled", "in-progress", "completed", "cancelled"]
location: { // GeoJSON Point
type: String,
coordinates: [Number]
},
breaks: [{ // Array of break periods
startTime: Date,
endTime: Date,
type: String, // ["lunch", "rest", "other"]
active: Boolean
}],
// Payout related fields
payoutRate: Number,
payoutHourlyTotal: Number,
payoutTotal: Number,
payoutSupplemental: Number,
payoutType: String, // ["Flat", "Hourly"]
payoutId: String,
overtime: Number,
overtimeRate: Number,
department: String,
parentCompany: String
}
API Endpointsβ
Shiftsβ
| Method | Endpoint | Description | Access |
|---|---|---|---|
| GET | /api/v1/shifts | Get all shifts with pagination | Private |
| GET | /api/v1/shifts/:id | Get shift by ID | Private |
| POST | /api/v1/shifts | Create new shift | Private |
| PUT | /api/v1/shifts/:id | Update shift | Private |
| DELETE | /api/v1/shifts | Delete shift(s) | Private/Admin |
| PUT | /api/v1/shifts/:shiftId/break | Add/update break | Private |
Mobile Implementationβ
The mobile app uses a React Context-based implementation through the useShift hook:
Featuresβ
- Real-time time tracking
- Break management
- Persistent state using local storage
- Integration with job/task management
- Offline support
Usage Exampleβ
const {
active, // Is shift active
startShift, // Start new shift
endShift, // End current shift
shift, // Current shift data
elapsedTime, // Formatted elapsed time
startBreak, // Start break
endBreak, // End break
breakActive, // Is break active
breakElapsedTime, // Formatted break time
} = useShift();
Time Calculationsβ
The system uses millisecond-based calculations for precise time tracking:
- Total Time: End time - Start time
- Break Time: Sum of all break durations
- Working Time: Total time - Break time
- Overtime: Working time beyond 8 hours
Overtime Calculationβ
const EIGHT_HOURS_MS = 8 * 60 * 60 * 1000;
const overtimeMs =
workingTime > EIGHT_HOURS_MS ? workingTime - EIGHT_HOURS_MS : 0;
Payroll Integrationβ
Shifts integrate with the payroll system through the Payout model:
- Automatic rate calculations
- Support for regular and overtime hours
- Custom rate overrides
- Break time deductions
- Integration with job/task completion
Job Integrationβ
The shift system integrates with the job management system:
- Tracks time spent on specific tasks
- Prevents shift completion with pending tasks
- Supports task pausing and resuming
- Maintains accurate billable hours
Securityβ
All shift endpoints are protected with:
- Token verification
- User authentication
- Parent company verification
- Role-based access control
Best Practicesβ
-
Time Tracking
- Always use millisecond precision
- Store times in UTC
- Use server time for critical operations
-
Break Management
- Validate break transitions
- Prevent overlapping breaks
- Track break types separately
-
Data Integrity
- Validate shift state transitions
- Prevent backdated modifications
- Maintain audit trails
-
Performance
- Use pagination for shift lists
- Index frequently queried fields
- Optimize time calculations
Error Handlingβ
Common error scenarios and handling:
-
Incomplete Tasks
if (inProgressItems.length > 0) {
return {
status: "error",
message: "Complete pending tasks before ending shift",
data: { pendingItems: inProgressItems },
};
} -
Invalid Break Operations
if (!currentBreak) {
return {
status: "error",
message: "No active break found to end",
};
}
Future Enhancementsβ
-
Planned Features
- Geofencing support
- Advanced break rules
- Shift templates
- Schedule integration
-
Optimization Opportunities
- Caching improvements
- Real-time updates
- Offline sync
- Batch operations
Known Issues and Solutionsβ
Break Type Validationβ
Issue: Break types are inconsistently enforced across the system.
Solution:
- Add middleware validation:
const validateBreakType = (req, res, next) => {
const validTypes = ["lunch", "rest", "other"];
if (req.body.breakType && !validTypes.includes(req.body.breakType)) {
return res.status(400).json({
status: "error",
message: "Invalid break type. Must be one of: lunch, rest, other",
});
}
next();
};
- Add schema-level validation:
breaks: [
{
type: {
type: String,
enum: {
values: ["lunch", "rest", "other"],
message: "Invalid break type",
},
required: true,
},
},
];
Clock In/Out Issuesβ
Issue: Users experience clock in/out problems due to various edge cases.
Solutions:
- Concurrent Shift Prevention:
const preventConcurrentShifts = async (employeeId) => {
const activeShift = await Shift.findOne({
employee: employeeId,
endTime: null,
status: "in-progress",
});
if (activeShift) {
throw new Error("Employee already has an active shift");
}
};
- Timezone Handling:
// Store all times in UTC
const startTime = new Date().toISOString();
// Convert to local time for display
const localStartTime = new Date(shift.startTime).toLocaleString();
- Network Resilience:
// Mobile app
const handleClockIn = async () => {
try {
// Save locally first
await Storage.setItem("pendingClockIn", {
time: new Date().toISOString(),
type: "clockIn",
});
// Attempt server sync
await startShift();
// Clear local storage on success
await Storage.removeItem("pendingClockIn");
} catch (error) {
// Handle offline mode
setOfflineMode(true);
}
};
- Time Validation:
const validateShiftTimes = (startTime, endTime) => {
// Prevent future start times
if (startTime > new Date()) {
throw new Error("Cannot start shift in the future");
}
// Prevent end time before start time
if (endTime && endTime < startTime) {
throw new Error("Shift cannot end before it starts");
}
// Prevent extremely long shifts
const MAX_SHIFT_HOURS = 24;
if (endTime && endTime - startTime > MAX_SHIFT_HOURS * 60 * 60 * 1000) {
throw new Error(`Shift cannot be longer than ${MAX_SHIFT_HOURS} hours`);
}
};
- Device/Server Time Sync:
const syncTimeWithServer = async () => {
const serverTime = await fetchServerTime();
const localTime = new Date();
const timeDiff = serverTime - localTime;
if (Math.abs(timeDiff) > 5 * 60 * 1000) {
// 5 minutes
throw new Error("Device time significantly different from server time");
}
return timeDiff;
};
Best Practices for Implementationβ
-
Break Management:
- Always specify break type
- Validate break duration limits
- Prevent overlapping breaks
- Track mandatory break compliance
-
Clock Operations:
- Use server time as source of truth
- Implement offline queue for operations
- Add retry logic for failed operations
- Validate against schedule if applicable
-
Data Integrity:
- Add database constraints
- Implement transaction handling
- Add audit logging for time changes
- Regular data validation checks
-
User Experience:
- Clear error messages
- Offline mode indicators
- Sync status visibility
- Confirmation for critical actions
Monitoring and Alertsβ
-
System Health:
- Track failed clock operations
- Monitor time discrepancies
- Alert on validation failures
- Track offline sync success rate
-
User Patterns:
- Monitor unusual shift patterns
- Track break compliance
- Identify common error scenarios
- Analyze usage patterns