Spaces:
Sleeping
Sleeping
Payroll System - Quick Reference Guide
Overview
Weekly payroll generation system that automatically calculates worker compensation from timesheets and ticket assignments.
Key Concepts
Pay Period
- Duration: Weekly (Monday to Sunday)
- Generation: Monday morning (6 AM) for previous week
- Calculation: Aggregates timesheets + ticket assignments
Compensation Formula
total_amount = flat_rate + ticket_earnings + bonus - deductions
where:
flat_rate = Weekly base rate from project_role
ticket_earnings = (base_amount * tickets_closed) + commission
commission = (base_amount * tickets_closed) * (commission_percentage / 100)
API Endpoints
Generate Payroll (Manual)
POST /api/v1/payroll/generate
Authorization: Bearer {token}
Content-Type: application/json
{
"user_id": "uuid",
"project_id": "uuid",
"period_start_date": "2024-12-09", // Must be Monday
"period_end_date": "2024-12-15" // Must be Sunday
}
Response: PayrollGenerateResponse
{
"success": true,
"payroll": { ... },
"message": "Payroll generated successfully",
"skipped_reason": null
}
Generate Batch Payroll
POST /api/v1/payroll/generate-batch
Authorization: Bearer {token}
Content-Type: application/json
{
"target_date": "2024-12-09", // Any date in target week
"project_id": null // Optional: filter by project
}
Response: PayrollBatchGenerateResponse
{
"success": true,
"total_processed": 25,
"total_generated": 20,
"total_skipped": 5,
"payrolls": [...],
"errors": [],
"period_start_date": "2024-12-09",
"period_end_date": "2024-12-15"
}
Get Payroll Details
GET /api/v1/payroll/{payroll_id}
Authorization: Bearer {token}
Response: PayrollResponse
{
"id": "uuid",
"user_id": "uuid",
"project_id": "uuid",
"period_start_date": "2024-12-09",
"period_end_date": "2024-12-15",
"period_label": "Dec 09 - Dec 15, 2024",
"tickets_closed": 12,
"hours_worked": 40.0,
"days_worked": 5,
"flat_rate_amount": 5000.00,
"ticket_earnings": 6600.00,
"bonus_amount": 0.00,
"deductions": 0.00,
"total_amount": 11600.00,
"net_earnings": 11600.00,
"is_paid": false,
"can_recalculate": true,
"is_current_week": false,
"user_name": "John Doe",
"user_email": "john@example.com",
"project_name": "FTTH Rollout",
...
}
List Payrolls (Filtered)
GET /api/v1/payroll?user_id={uuid}&is_paid=false&page=1&page_size=20
Authorization: Bearer {token}
Query Parameters:
- user_id: Filter by specific worker
- project_id: Filter by project
- is_paid: Filter by payment status (true/false)
- period_start_date: Filter by period start (>=)
- period_end_date: Filter by period end (<=)
- page: Page number (default: 1)
- page_size: Items per page (default: 20, max: 100)
- sort_by: Sort field (period_start_date, total_amount, created_at, paid_at)
- sort_order: Sort order (asc/desc)
Response: PayrollListResponse
{
"items": [...],
"total": 50,
"page": 1,
"page_size": 20,
"has_more": true
}
Recalculate Payroll
POST /api/v1/payroll/{payroll_id}/recalculate
Authorization: Bearer {token}
Response: PayrollGenerateResponse
{
"success": true,
"payroll": { ... },
"message": "Payroll recalculated successfully"
}
Note: Only works on unpaid payrolls
Mark as Paid
POST /api/v1/payroll/{payroll_id}/mark-paid
Authorization: Bearer {token}
Content-Type: application/json
{
"payment_method": "mobile_money", // mobile_money, bank_transfer, cash, cheque
"payment_reference": "ABC123XYZ" // Optional: M-Pesa ID, bank ref, etc.
}
Response: PayrollResponse
{
"id": "uuid",
"is_paid": true,
"paid_at": "2024-12-16T08:30:00Z",
"paid_by_user_id": "uuid",
"payment_method": "mobile_money",
"payment_reference": "ABC123XYZ",
...
}
Note: Cannot be recalculated after marking as paid
CLI Commands
Generate Weekly Payroll
# Default: Previous week
python -m app.tasks.payroll_tasks
# Dry run (test without committing)
python -m app.tasks.payroll_tasks --dry-run
# Specific date (calculates week containing this date)
python -m app.tasks.payroll_tasks --date 2024-12-09
# Specific project only
python -m app.tasks.payroll_tasks --project-id abc123...
# Ad-hoc week (catch-up for missed week)
python -m app.tasks.payroll_tasks --start 2024-12-02 --end 2024-12-08
Output Example
===== PAYROLL GENERATION JOB STARTED =====
Target date: 2024-12-08
Project filter: ALL
Processing payroll for week: 2024-12-02 to 2024-12-08
===== PAYROLL GENERATION SUMMARY =====
Total processed: 25
Total generated: 20
Total skipped: 5
Errors: 0
===== PAYROLL GENERATION JOB COMPLETED =====
Scheduling
Linux/Mac (Cron)
# Edit crontab
crontab -e
# Add line: Every Monday at 6 AM
0 6 * * 1 cd /path/to/project && python -m app.tasks.payroll_tasks
Windows (Task Scheduler)
- Open Task Scheduler
- Create Task: "Weekly Payroll Generation"
- Trigger: Weekly, Monday, 6:00 AM
- Action: Start Program
- Program:
python.exe - Arguments:
-m app.tasks.payroll_tasks - Working Directory:
D:\atomio\swiftops-backend
- Program:
Common Use Cases
1. Review Unpaid Payrolls (Weekly)
# API call
curl http://localhost:8000/api/v1/payroll?is_paid=false \
-H "Authorization: Bearer $TOKEN"
# Response: List of all unpaid payrolls
# Review, adjust bonuses/deductions if needed
2. Correct Timesheet (Triggers Auto-Recalculation)
# Manager updates timesheet
# System automatically recalculates affected payroll (if unpaid)
# No manual action needed!
3. Add Bonus to Payroll
# Not yet implemented in API - use direct DB update
UPDATE user_payroll
SET bonus_amount = 1000,
calculation_notes = calculation_notes || ' | Bonus: Performance award',
total_amount = total_amount + 1000
WHERE id = 'payroll-uuid' AND is_paid = false;
# Future: POST /api/v1/payroll/{id}/update endpoint
4. Pay All Workers for Week
# 1. List unpaid payrolls for week
curl "http://localhost:8000/api/v1/payroll?is_paid=false&period_start_date=2024-12-09"
# 2. For each payroll, mark as paid
curl -X POST http://localhost:8000/api/v1/payroll/{id}/mark-paid \
-H "Authorization: Bearer $TOKEN" \
-d '{"payment_method": "mobile_money", "payment_reference": "MPESA123"}'
5. Generate Payroll for Missed Week
# Run ad-hoc generation
python -m app.tasks.payroll_tasks --start 2024-11-25 --end 2024-12-01
# Or via API
curl -X POST http://localhost:8000/api/v1/payroll/generate-batch \
-H "Authorization: Bearer $TOKEN" \
-d '{"target_date": "2024-11-27"}'
6. Recalculate After Data Correction
# If timesheet correction didn't trigger auto-recalc (paid payroll)
# Manual recalculation needed
curl -X POST http://localhost:8000/api/v1/payroll/{id}/recalculate \
-H "Authorization: Bearer $TOKEN"
Authorization
Who Can Do What?
Platform Admins:
- β Generate payroll (manual/batch)
- β View all payrolls
- β Recalculate any unpaid payroll
- β Mark payroll as paid
Project Managers:
- β Generate payroll for their projects
- β View payrolls for their projects
- β Recalculate unpaid payrolls in their projects
- β Mark payroll as paid in their projects
Dispatchers:
- β View payrolls (read-only)
- β Cannot generate/modify
Workers:
- β View their own payroll only
- β Cannot generate/modify
Troubleshooting
Payroll Not Generated?
Check:
- Is worker in project_team? (active member)
- Does worker have project_role assigned?
- Are there timesheets for that week?
- Was payroll already generated? (check logs)
Payroll Amount is Zero?
Check:
- Worker's role has flat_rate_amount set?
- Worker's role has base_amount and commission_percentage set?
- Worker closed any tickets that week?
- Timesheets show PRESENT status?
Cannot Recalculate?
Check:
- Is payroll already paid? (cannot recalculate paid)
- Check
is_paidandcan_recalculatefields
Background Job Not Running?
Check:
- Cron/Task Scheduler configured correctly?
- Python environment activated?
- Working directory set correctly?
- Check logs:
payroll_job_YYYYMMDD_HHMMSS.log
Data Sources
Timesheets
-- Aggregated data
SELECT
user_id,
project_id,
COUNT(*) FILTER (WHERE status = 'present') as days_worked,
SUM(hours_worked) FILTER (WHERE status = 'present') as hours_worked
FROM timesheets
WHERE work_date BETWEEN '2024-12-09' AND '2024-12-15'
GROUP BY user_id, project_id;
Ticket Assignments
-- Aggregated data
SELECT
user_id,
COUNT(*) as tickets_closed
FROM ticket_assignments
WHERE action = 'complete'
AND ended_at BETWEEN '2024-12-09 00:00:00' AND '2024-12-15 23:59:59'
GROUP BY user_id;
Compensation Rates
-- From project_team + project_role
SELECT
pt.user_id,
pt.project_id,
pr.flat_rate_amount,
pr.base_amount,
pr.commission_percentage,
pr.role_name
FROM project_team pt
JOIN project_roles pr ON pt.project_role_id = pr.id
WHERE pt.user_id = ? AND pt.project_id = ?;
Best Practices
1. Weekly Workflow
- Monday morning: Background job generates payroll automatically
- Monday-Tuesday: Review generated payrolls, add bonuses/deductions
- Wednesday: Mark payrolls as paid after disbursement
- Thursday-Friday: Workers check their payroll via app
2. Data Integrity
- Ensure timesheets generated daily (don't wait until week end)
- Ensure ticket assignments properly closed
- Ensure all workers have roles assigned
- Validate compensation rates are up-to-date
3. Error Handling
- Check batch job logs every Monday
- Investigate skipped workers
- Fix missing data (roles, timesheets)
- Re-run for specific workers if needed
4. Testing
- Always dry-run first in new environment
- Test with one worker before batch
- Verify calculations manually first time
- Monitor closely for first 2-3 weeks
Example Workflow
Complete Payroll Cycle
# 1. Monday 6 AM: Background job runs (automated)
# Generates payroll for previous week
# 2. Monday 9 AM: Manager reviews unpaid payrolls
curl http://localhost:8000/api/v1/payroll?is_paid=false
# 3. Manager adds bonus for top performer
# (Future API endpoint - currently manual DB update)
# 4. Tuesday: Manager corrects timesheet for sick leave
# System auto-recalculates affected payroll
# 5. Wednesday: Disburse payments (M-Pesa, bank transfer)
# Then mark as paid
curl -X POST http://localhost:8000/api/v1/payroll/{id}/mark-paid \
-d '{"payment_method": "mobile_money", "payment_reference": "MPESA123"}'
# 6. Workers check their payroll via mobile app
# GET /api/v1/payroll (auto-filtered to their own)
Support
Get Help
- Documentation:
docs/agent/PAYROLL_IMPLEMENTATION_COMPLETE.md - Logs:
payroll_job_YYYYMMDD_HHMMSS.log - Database:
user_payrolltable - API Docs:
http://localhost:8000/docs(Swagger UI)
Report Issues
Include:
- Payroll ID (if applicable)
- User ID and Project ID
- Period dates
- Expected vs actual amounts
- Relevant logs