Skip to main content
Back to ScopeForged

ScopeForged Documentation

Technical documentation, guides, and feature references for the ScopeForged client portal.

Core Business Features/Time Tracking

Time Tracking & Resource Management Guide

Last Updated: 2026-01-12 Status: Implemented Plan Reference: 044-time-tracking-resource-management.md, 064-time-tracking-enhancement.md, 088-time-tracking-improvement.md


Overview

The Time Tracking & Resource Management system provides comprehensive time tracking with project allocation, team capacity planning, resource utilization analytics, and timesheet management. It enables accurate project costing, team workload visibility, and billable hours tracking for invoicing.


Table of Contents

  1. Accessing Time Tracking
  2. Time Entries
  3. Bulk Time Entry
  4. Timer Widget
  5. Mobile Timer Widget
  6. Cross-Device Timer Sync
  7. Keyboard Shortcuts
  8. Timesheets
  9. Timesheet Approval Workflow
  10. Billable Time & Invoice Integration
  11. Productivity Reports
  12. Resource Planning
  13. Technical Architecture
  14. Related Features

Accessing Time Tracking

Access PointLocationURLRole
Time EntriesAdmin sidebar/admin/timeAdmin
TimesheetsAdmin sidebar/admin/timesheetsAdmin
Resource PlanningAdmin sidebar/admin/resourcesAdmin
Team CapacityResources page/admin/resources/capacityAdmin
TasksAdmin sidebar/admin/tasksAdmin

Permissions

ActionAdminClient User
Log time entries
View own entries
Submit timesheet
Approve timesheets✅ (Managers)
View resource planning
Manage allocations

Time Entries

Creating Manual Entries

  1. Navigate to Admin → Time Entries
  2. Click "New Entry"
  3. Fill in entry details:
    • Project: Select the project
    • Task (optional): Select specific task
    • Date: Entry date
    • Duration: Time spent in hours/minutes
    • Description: What you worked on
    • Billable: Mark if billable
  4. Click "Save Entry"

Editing Entries

  1. Navigate to Admin → Time Entries
  2. Click on an entry row
  3. Modify fields as needed
  4. Click "Update Entry"

Entry Status Workflow

Pending → Submitted → Approved
                   ↘ Rejected (back to Pending)
StatusDescription
pendingEntry created, not yet submitted
approvedApproved by manager
rejectedRejected, needs revision

Filtering Entries

  • By Project: Filter to specific project
  • By Date Range: Select start and end dates
  • By Status: Filter by approval status
  • By Billable: Filter billable/non-billable

Bulk Time Entry

The bulk entry feature allows logging multiple time entries at once, ideal for end-of-week time entry or catching up on missed days.

Accessing Bulk Entry

  1. Navigate to Admin → Time Entries
  2. Click "Bulk Entry" button
  3. Or use keyboard shortcut Alt + B
  4. Or navigate directly to /admin/time/bulk

Using Bulk Entry

  1. The form starts with one empty row
  2. Fill in:
    • Date: Entry date
    • Project: Select from dropdown
    • Hours: Duration (0.25 - 24)
    • Description: What you worked on
    • Billable: Toggle checkbox
  3. Click "Add Row" for more entries
  4. Click "Add 5 Rows" to add multiple at once
  5. Click "Save All Entries" when complete

Features

  • Smart Defaults: New rows inherit date and project from previous row
  • Total Calculation: Running total of hours shown in header and footer
  • Validation: Each entry is validated before saving
  • Limit: Maximum 50 entries per submission

Validation Rules

FieldRules
DateRequired, valid date, not future
ProjectRequired, must exist
HoursRequired, 0.25 - 24
DescriptionOptional, max 500 characters

API Endpoint

POST /admin/time/bulk
{
  "entries": [
    {
      "date": "2026-01-12",
      "project_id": 1,
      "hours": 2.5,
      "description": "Feature development",
      "is_billable": true
    }
  ]
}

Timer Widget

Starting a Timer

  1. Click the Timer icon in the navigation bar
  2. Select Project (required)
  3. Select Task (optional)
  4. Add Description (optional)
  5. Click "Start Timer"

Timer Controls

ActionButtonDescription
Start▶️ PlayBegin tracking time
Stop⏹️ StopStop and save entry
Discard🗑️ TrashCancel without saving

Timer Features

  • Real-time Display: Shows elapsed time
  • One Active Timer: Only one timer per user at a time
  • Auto-Save: Stopping creates a time entry
  • Persistent: Timer continues across page navigation

API Endpoints

# Start timer
POST /admin/time/timer/start
{
  "project_id": 1,
  "task_id": null,
  "description": "Working on feature",
  "is_billable": true
}

# Stop timer
POST /admin/time/timer/stop

# Get running timer
GET /admin/time/timer/running

Mobile Timer Widget

The mobile timer is a floating widget for quick time tracking on any page.

Accessing the Widget

The mobile timer appears as a floating button in the bottom-right corner of admin pages. It shows:

  • Clock Icon: When no timer running
  • Elapsed Time: When timer is active (e.g., "02:34:15")

Using the Mobile Timer

  1. Click the floating button to expand the widget
  2. Select Project from dropdown (required)
  3. Select Task if available (optional)
  4. Enter Description for the work
  5. Toggle Billable checkbox
  6. Click "Start" to begin tracking

Controls

ButtonAction
StartBegin tracking time
PausePause timer without saving
Stop & SaveStop and create time entry
DiscardCancel without saving

Features

  • Persistent State: Timer continues across page navigation
  • LocalStorage Backup: State saved locally for recovery
  • Minimum Duration Warning: Confirms if entry is less than 1 minute
  • Recent Entries: Shows last 5 time entries
  • Auto-Load Tasks: Tasks load when project is selected

Component Usage

<x-mobile-timer :projects="$projects" />

Including in Layout

Add to your layout file to enable on all pages:

@auth
    <x-mobile-timer :projects="$timerProjects" />
@endauth

Cross-Device Timer Sync

The timer synchronization feature allows users to seamlessly switch between devices while tracking time.

How It Works

  1. Device Registration: Each device is assigned a unique device ID
  2. State Synchronization: Timer state is synced via WebSocket events
  3. Conflict Resolution: Server maintains authoritative timer state
  4. Offline Support: Local elapsed time cached for offline use

Timer State Properties

PropertyDescription
elapsed_secondsTotal time elapsed on timer
is_runningWhether timer is currently active
device_idDevice that last modified timer
last_sync_atLast synchronization timestamp

Real-Time Events

The TimerUpdated event broadcasts timer changes:

// Event channels
private-timer.{userId}

// Event payload
{
    "action": "started|paused|resumed|stopped",
    "timer": { ... timer state ... },
    "device_id": "device-uuid"
}

API Endpoints

# Sync timer state
POST /admin/time/timer/sync
{
  "device_id": "uuid",
  "elapsed_seconds": 3600,
  "is_running": true
}

# Pause timer (cross-device)
POST /admin/time/timer/pause

# Resume timer (cross-device)
POST /admin/time/timer/resume

# Resume from existing entry
POST /admin/time/timer/resume-from-entry
{
  "time_entry_id": 123
}

Keyboard Shortcuts

Time tracking includes keyboard shortcuts for quick access to common actions.

Available Shortcuts

ShortcutActionDescription
Alt + TToggle TimerStart or pause the active timer
Alt + SStop TimerStop timer and save the entry
Alt + NNew EntryOpen new manual time entry form
Alt + EExpand/CollapseToggle mobile timer widget
Alt + BBulk EntryOpen bulk time entry page

How It Works

Shortcuts are handled by resources/js/time-tracking/keyboard-shortcuts.js:

import { initTimeTrackingShortcuts } from './time-tracking/keyboard-shortcuts';

// Initialize with default handlers
initTimeTrackingShortcuts();

// Or with custom handlers
initTimeTrackingShortcuts({
    onToggle: () => customToggle(),
    onStop: () => customStop(),
    onNewEntry: () => openCustomModal(),
});

Input Field Behavior

Keyboard shortcuts are disabled when typing in:

  • Text inputs
  • Textareas
  • Select dropdowns
  • Content-editable elements

This prevents accidental navigation while entering data.

Showing Help

Display a shortcuts help modal:

import { showShortcutsHelp } from './time-tracking/keyboard-shortcuts';

// Show modal with all available shortcuts
showShortcutsHelp();

Timesheets

Weekly View

  1. Navigate to Admin → Timesheets
  2. View current week's entries by day
  3. See daily and weekly totals
  4. Navigate weeks with arrow buttons

Timesheet Components

SectionContent
Day GridEntries organized by day
Project SummaryTime grouped by project
TotalsWeekly hours (total/billable)
StatusDraft/Submitted/Approved

Submitting Timesheets

  1. Complete all entries for the week
  2. Review totals and accuracy
  3. Click "Submit for Approval"
  4. Status changes to "Submitted"

Approving Timesheets (Managers)

  1. Navigate to Admin → Timesheets → Pending
  2. Review submitted timesheet
  3. Click "Approve" or "Reject"
  4. If rejecting, provide reason

Timesheet Status

StatusDescription
draftIn progress, not submitted
submittedAwaiting approval
approvedApproved by manager
rejectedNeeds revision
changes_requestedManager requested changes

Timesheet Approval Workflow

The enhanced approval workflow provides granular control over timesheet review.

Approval Actions

ActionDescriptionAvailable When
ApproveAccept timesheet with optional notesSubmitted, Changes Requested
RejectReject with required reasonSubmitted, Changes Requested
Request ChangesAsk for specific modificationsSubmitted, Changes Requested
ReopenReturn rejected timesheet to draftRejected (owner only)

Requesting Changes

Managers can request specific changes without fully rejecting:

  1. Navigate to Admin → Timesheets → Pending
  2. Click on a timesheet to review
  3. Click "Request Changes"
  4. Enter message describing needed changes
  5. Timesheet status changes to changes_requested
  6. Employee receives notification with message

Change Request Tracking

Each change request is stored with:

  • Requested By: Manager who requested
  • Message: Description of changes needed
  • Changes: Specific entries to modify (optional)
  • Status: pending, addressed, dismissed
  • Addressed At: When changes were made

Approval with Notes

Managers can add notes when approving:

  1. Click "Approve" on timesheet
  2. Enter optional notes for the employee
  3. Notes are stored and visible to employee
  4. Approval notification includes notes

Notifications

EventRecipientChannels
SubmittedManagersEmail, Database
ApprovedEmployeeEmail, Database
RejectedEmployeeEmail, Database
Changes RequestedEmployeeEmail, Database

Bulk Approval

For efficiency, managers can approve multiple timesheets:

  1. Navigate to Admin → Timesheets → All
  2. Select timesheets with checkboxes
  3. Click "Bulk Approve"
  4. All selected timesheets are approved

Routes

GET  /admin/timesheets                    # User's timesheet
GET  /admin/timesheets/pending            # Pending approvals
GET  /admin/timesheets/all                # All timesheets (admin)
GET  /admin/timesheets/statistics         # Weekly statistics
GET  /admin/timesheets/{timesheet}        # Review timesheet
POST /admin/timesheets/{timesheet}/submit # Submit for approval
POST /admin/timesheets/{timesheet}/approve # Approve
POST /admin/timesheets/{timesheet}/reject  # Reject
POST /admin/timesheets/{timesheet}/request-changes # Request changes
POST /admin/timesheets/{timesheet}/reopen  # Reopen rejected
POST /admin/timesheets/bulk-approve        # Bulk approve

Billable Time & Invoice Integration

The billable time integration streamlines creating invoices from tracked time.

Finding Unbilled Time

  1. Navigate to Admin → Clients → [Client] → Invoices
  2. Click "View Unbilled Time"
  3. See all approved, unbilled entries for client

Creating Invoice from Time

  1. Click "Create Invoice from Time"
  2. Select time entries to include
  3. Preview generated invoice items
  4. Confirm and create invoice
  5. Time entries automatically marked as billed

Invoice Preview

Before creating, preview shows:

  • Grouped entries by project
  • Total hours per project
  • Calculated amounts using hourly rates
  • Overall invoice total

Unbilled Summary

View summary of unbilled time per client:

$summary = $billableTimeService->getUnbilledSummary($clientId);
// Returns:
// - total_entries
// - total_hours
// - total_amount
// - by_project (breakdown)

Unlinking Time from Invoice

If needed, time entries can be unlinked:

  1. Navigate to the invoice
  2. Find linked time entries
  3. Click "Unlink" to remove association
  4. Entries return to unbilled status

API Methods

// Get billable entries for client
$entries = $billableTimeService->getBillableEntries($clientId, $filters);

// Create invoice from time entries
$invoice = $billableTimeService->createInvoiceFromTime($client, $entryIds);

// Preview invoice items
$items = $billableTimeService->previewInvoiceItems($entryIds);

// Unlink entries from invoice
$billableTimeService->unlinkFromInvoice($invoice);

Productivity Reports

The TimeReportsService provides comprehensive analytics on time tracking data.

User Productivity Report

View individual user productivity metrics for any date range:

Navigate to: Admin → Time → Reports → Productivity

Metrics Included:

  • Total hours worked
  • Billable vs non-billable hours
  • Billable percentage
  • Average hours per day
  • Working days count
  • Hours by project
  • Daily breakdown

API Endpoint:

GET /admin/time/reports/productivity?start_date=2026-01-01&end_date=2026-01-31&user_id=1

Service Method:

$service = app(TimeReportsService::class);
$report = $service->getUserProductivity($userId, $startDate, $endDate);

// Returns:
// [
//     'total_hours' => 160,
//     'billable_hours' => 128,
//     'non_billable_hours' => 32,
//     'billable_percentage' => 80.0,
//     'average_per_day' => 8.0,
//     'working_days' => 20,
//     'by_project' => [...],
//     'daily_breakdown' => [...],
// ]

Team Capacity Report

View team utilization and availability for any week:

Navigate to: Admin → Time → Reports → Capacity

Metrics Per User:

  • Logged hours
  • Capacity hours (default 40)
  • Utilization percentage
  • Remaining hours
  • Over-capacity flag
  • Billable hours

API Endpoint:

GET /admin/time/reports/capacity?week=2026-01-06

Service Method:

$capacity = $service->getTeamCapacity($weekDate);

// Returns Collection with each team member's:
// - user_id, user_name
// - logged_hours, capacity_hours
// - utilization (percentage)
// - remaining_hours
// - is_over_capacity
// - billable_hours

Project Budget Utilization

Track project hours against budget:

$utilization = $service->getProjectBudgetUtilization($projectId);

// Returns:
// [
//     'project_name' => 'Website Redesign',
//     'total_hours' => 50,
//     'budget_hours' => 100,
//     'utilization' => 50.0,
//     'remaining_hours' => 50,
//     'is_over_budget' => false,
//     'by_user' => [...],
//     'by_status' => [...],
// ]

Timesheet Summary

Get weekly summary for a user:

$summary = $service->getTimesheetSummary($userId, $weekDate);

// Returns:
// [
//     'week_start' => '2026-01-06',
//     'week_end' => '2026-01-12',
//     'total_hours' => 40,
//     'billable_hours' => 32,
//     'pending_approval' => 5,
//     'approved' => 15,
//     'by_day' => [...],
//     'by_project' => [...],
// ]

Billable Summary for Invoicing

Get unbilled time for a client:

$summary = $service->getBillableSummary($clientId, $startDate, $endDate);

// Returns:
// [
//     'period_start' => '2026-01-01',
//     'period_end' => '2026-01-31',
//     'total_hours' => 80,
//     'total_amount' => 12000,
//     'entries_count' => 45,
//     'by_project' => [
//         [
//             'project_id' => 1,
//             'project_name' => 'API Development',
//             'hours' => 40,
//             'hourly_rate' => 150,
//             'amount' => 6000,
//         ],
//         ...
//     ],
// ]

Resource Planning

Project Allocations

Allocate team members to projects with scheduled hours.

Creating Allocation:

  1. Navigate to Admin → Resources → Allocations
  2. Click "New Allocation"
  3. Select User and Project
  4. Set Start Date and End Date (optional)
  5. Enter Hours per Week
  6. Set Hourly Rate (optional)
  7. Add Role description (optional)
  8. Click "Save"

Team Capacity View

View team utilization and availability.

MetricDescription
Total CapacityWeekly hours available (default 40)
AllocatedHours assigned to projects
LoggedActual hours tracked
BillableBillable hours tracked
Time OffVacation/sick hours
AvailableCapacity - Allocated - Time Off

Utilization Report

See team efficiency metrics:

Utilization % = (Billable Hours / Total Capacity) × 100

Report Features:

  • Filter by date range
  • View by team member
  • Export to CSV
  • Trend visualization

Finding Available Resources

  1. Navigate to Admin → Resources → Capacity
  2. Select Week to view
  3. Enter Hours Needed
  4. View team members with availability
  5. Create allocation from results

Tasks

Creating Tasks

  1. Navigate to Admin → Tasks
  2. Click "New Task"
  3. Fill in task details:
    • Name: Task title
    • Project: Parent project
    • Assigned To: Team member
    • Description: Task details
    • Estimated Hours: Time estimate
    • Priority: Low/Medium/High/Urgent
    • Due Date: Target completion

Task Status

StatusDescription
pendingNot started
in_progressCurrently working
completedFinished
cancelledCancelled

Task Time Tracking

  • Time entries can link to tasks
  • Estimated Hours: Initial estimate
  • Actual Hours: Sum of time entries
  • Variance: Actual - Estimated

Time Off Management

Requesting Time Off

  1. Navigate to Admin → Time Off
  2. Click "Request Time Off"
  3. Select Type: Vacation, Sick, Personal, Holiday
  4. Enter Start Date and End Date
  5. Specify Hours (or calculates automatically)
  6. Add Notes (optional)
  7. Click "Submit Request"

Time Off Types

TypeDescription
vacationPlanned time off
sickIllness
personalPersonal days
holidayCompany holidays

Approval Workflow

Pending → Approved
        ↘ Rejected

Billable Time & Invoicing

Marking Time as Billable

  • Default billable setting per project
  • Can override per entry
  • Tracks hourly rate from allocation or user default

Linking to Invoices

  1. Navigate to client's invoices
  2. Click "Create Invoice"
  3. Select "Include Unbilled Time"
  4. Choose entries to include
  5. Time entries marked as billed

Viewing Unbilled Time

  1. Navigate to Clients → [Client] → Invoices
  2. Click "Unbilled Time"
  3. View all approved, unbilled entries
  4. Filter by project or date range

Technical Architecture

Models

Location: app/Models/

ModelTablePurpose
TimeEntrytime_entriesIndividual time records
RunningTimerrunning_timersActive timers
TimesheettimesheetsWeekly time summaries
TimesheetChangeRequesttimesheet_change_requestsApproval change requests
TasktasksProject tasks
ProjectAllocationproject_allocationsResource assignments
TeamCapacityteam_capacityWeekly capacity records
TimeOffRequesttime_off_requestsTime off records

Services

Location: app/Services/TimeTracking/

// TimeTrackingService - Core time tracking operations
class TimeTrackingService
{
    public function startTimer(User $user, array $data): RunningTimer;
    public function stopTimer(User $user): ?TimeEntry;
    public function getRunningTimer(User $user): ?RunningTimer;
    public function createManualEntry(User $user, array $data): TimeEntry;
    public function getTimesheet(User $user, Carbon $weekStart): Timesheet;
    public function getWeeklyReport(User $user, Carbon $weekStart): array;
    public function getUnbilledTime(int $clientId): Collection;
    public function markAsBilled(array $entryIds, int $invoiceId): int;
}

// TimerSyncService - Cross-device timer synchronization
class TimerSyncService
{
    public function syncTimer(User $user, array $state): RunningTimer;
    public function getTimerState(User $user): ?array;
    public function startTimer(User $user, array $data): RunningTimer;
    public function pauseTimer(User $user): RunningTimer;
    public function resumeTimer(User $user): RunningTimer;
    public function stopTimer(User $user): TimeEntry;
    public function resumeFromEntry(User $user, TimeEntry $entry): RunningTimer;
}

// TimesheetApprovalService - Approval workflow management
class TimesheetApprovalService
{
    public function submitForApproval(Timesheet $timesheet): Timesheet;
    public function approve(Timesheet $timesheet, User $approver, ?string $notes): Timesheet;
    public function reject(Timesheet $timesheet, User $rejector, string $reason): Timesheet;
    public function requestChanges(Timesheet $timesheet, User $requester, array $changes, ?string $message): TimesheetChangeRequest;
    public function addressChangesAndResubmit(Timesheet $timesheet, TimesheetChangeRequest $request): Timesheet;
    public function getPendingTimesheets(): Collection;
    public function getApprovalHistory(Timesheet $timesheet): Collection;
}

// BillableTimeService - Invoice integration
class BillableTimeService
{
    public function getBillableEntries(int $clientId, array $filters = []): Collection;
    public function createInvoiceFromTime(Client $client, array $entryIds): Invoice;
    public function getUnbilledSummary(int $clientId): array;
    public function getClientsWithUnbilledTime(): Collection;
    public function previewInvoiceItems(array $entryIds): array;
    public function unlinkFromInvoice(Invoice $invoice): int;
}

// ResourcePlanningService - Team capacity management
class ResourcePlanningService
{
    public function allocateToProject(User $user, Project $project, array $data);
    public function updateCapacity(User $user, Carbon $weekStart): TeamCapacity;
    public function getTeamCapacity(Carbon $weekStart): Collection;
    public function getUtilizationReport(Carbon $start, Carbon $end): array;
    public function findAvailableResources(Carbon $weekStart, int $hoursNeeded);
}

// TimeReportsService - Productivity and capacity reporting
// Location: app/Services/TimeReportsService.php
class TimeReportsService
{
    public function getUserProductivity(int $userId, Carbon $start, Carbon $end): array;
    public function getProjectBudgetUtilization(int $projectId): array;
    public function getTeamCapacity(Carbon $week): Collection;
    public function getTimesheetSummary(int $userId, Carbon $week): array;
    public function getBillableSummary(int $clientId, Carbon $start, Carbon $end): array;
}

Events

Location: app/Events/

EventChannelDescription
TimerUpdatedprivate-timer.{userId}Timer state changed

Controllers

Location: app/Http/Controllers/Admin/

ControllerRoutesPurpose
TimeEntryController/admin/time/*Time entry CRUD
TimesheetController/admin/timesheets/*Timesheet management
ResourceController/admin/resources/*Resource planning
TaskController/admin/tasks/*Task management
TimeOffController/admin/time-off/*Time off requests

Routes

// Time entries
Route::resource('time', TimeEntryController::class);
Route::post('time/timer/start', [TimeEntryController::class, 'startTimer']);
Route::post('time/timer/stop', [TimeEntryController::class, 'stopTimer']);
Route::get('time/timer/running', [TimeEntryController::class, 'runningTimer']);
Route::post('time/timer/update', [TimeEntryController::class, 'updateTimer']);
Route::post('time/timer/discard', [TimeEntryController::class, 'discardTimer']);

// Bulk Entry
Route::get('time/bulk', [TimeEntryController::class, 'bulkCreate']);
Route::post('time/bulk', [TimeEntryController::class, 'bulkStore']);
Route::post('time/bulk-approve', [TimeEntryController::class, 'bulkApprove']);
Route::post('time/bulk-reject', [TimeEntryController::class, 'bulkReject']);

// Reports
Route::get('time/reports/productivity', [TimeEntryController::class, 'productivityReport']);
Route::get('time/reports/capacity', [TimeEntryController::class, 'capacityReport']);

// AJAX Endpoints
Route::get('time/recent', [TimeEntryController::class, 'recentEntries']);
Route::get('time/projects/{project}/tasks', [TimeEntryController::class, 'projectTasks']);

// Timesheets
Route::get('timesheets', [TimesheetController::class, 'index']);
Route::post('timesheets/{timesheet}/submit', [TimesheetController::class, 'submit']);
Route::get('timesheets/pending', [TimesheetController::class, 'pending']);
Route::post('timesheets/{timesheet}/approve', [TimesheetController::class, 'approve']);
Route::post('timesheets/{timesheet}/reject', [TimesheetController::class, 'reject']);

// Resources
Route::get('resources', [ResourceController::class, 'index']);
Route::get('resources/capacity', [ResourceController::class, 'capacity']);
Route::get('resources/utilization', [ResourceController::class, 'utilization']);
Route::resource('allocations', AllocationController::class);

// Tasks and Time Off
Route::resource('tasks', TaskController::class);
Route::resource('time-off', TimeOffController::class);

Database Tables

Table: time_entries

ColumnTypeDescription
idbigintPrimary key
user_idbigintUser FK
project_idbigintProject FK
task_idbigintTask FK (nullable)
descriptionstringWork description
entry_datedateDate of work
duration_minutesintTime in minutes
is_billablebooleanBillable flag
is_billedbooleanBilled flag
is_lockedbooleanLocked for editing
invoice_idbigintInvoice FK (nullable)
hourly_ratedecimalRate for billing
statusstringpending/approved/rejected

Table: running_timers

ColumnTypeDescription
idbigintPrimary key
user_idbigintUser FK
project_idbigintProject FK
task_idbigintTask FK (nullable)
descriptionstringWork description
started_attimestampTimer start time
elapsed_secondsintAccumulated time
is_runningbooleanCurrently active
device_idstringOriginating device
last_sync_attimestampLast sync time

Table: timesheets

ColumnTypeDescription
idbigintPrimary key
user_idbigintUser FK
week_startdateWeek start date
week_enddateWeek end date
total_hoursintTotal hours
billable_hoursintBillable hours
statusstringdraft/submitted/approved/rejected/changes_requested
approved_bybigintApprover FK (nullable)
approved_attimestampApproval timestamp
approval_notestextApprover notes
rejected_bybigintRejector FK (nullable)
rejected_attimestampRejection timestamp
rejection_reasontextRejection reason

Table: timesheet_change_requests

ColumnTypeDescription
idbigintPrimary key
timesheet_idbigintTimesheet FK
requested_bybigintRequester FK
changesjsonRequested changes
messagetextRequest message
statusstringpending/addressed/dismissed
addressed_attimestampWhen addressed

Best Practices

For Time Tracking

  1. Track in real-time using the timer when possible
  2. Add descriptions for each entry
  3. Submit timesheets weekly for approval
  4. Review entries before submitting
  5. Use tasks to categorize work

For Resource Planning

  1. Allocate before projects start
  2. Update allocations when scope changes
  3. Monitor utilization weekly
  4. Plan for time off in advance
  5. Review capacity before committing

Troubleshooting

IssueSolution
Timer not startingCheck if another timer is running
Can't edit entryEntry may be approved, contact manager
Wrong project hoursVerify task assignments
Utilization too highCheck allocation overlaps
Missing billable timeVerify entries marked as billable

Dependencies

FeatureRelationship
Project ManagementProjects and tasks
InvoicingBillable time to invoices
AuthorizationApproval permissions

Complementary Features

FeatureDescription
AnalyticsTime analytics
ReportsTime reports
Admin DashboardTime overview

See Also