AgentSkillsCN

Time Entries

当您需要管理Autotask工时记录时,可使用此技能:记录工作小时数、进行计费计算、审批工时流程、追踪工时利用率,以及校验预算合理性。本技能覆盖工时记录字段、计费费率、审批状态、合同限额,同时与工单和项目实现无缝对接,是MSP技术人员追踪计费工时与非计费工时的必备工具。

SKILL.md
--- frontmatter
description: >
  Use this skill when working with Autotask time entries - logging work hours,
  billing calculations, approval workflows, utilization tracking, and budget
  validation. Covers time entry fields, billing rates, approval statuses,
  contract limits, and integration with tickets and projects.
  Essential for MSP technicians tracking billable and non-billable work.
triggers:
  - autotask time entry
  - log time
  - time tracking
  - billable hours
  - time entry approval
  - billing rate
  - utilization rate
  - time billing
  - work log
  - timesheet
  - hours worked
  - submit timesheet
  - approve time

Autotask Time Entry Management

Overview

Time entries are the foundation of MSP billing and resource utilization tracking. Every hour logged against tickets, projects, and contracts flows through the time entry system. This skill covers comprehensive time management including billing calculations, approval workflows, budget validation, and utilization analytics.

Approval Status Codes

Based on the Autotask API, these are the time entry approval statuses:

Status IDNameDescriptionBusiness Logic
0DRAFTEntry created but not submittedEditable by resource
1SUBMITTEDSubmitted for approvalLocked, awaiting manager review
2APPROVEDManager approved entryIncluded in billing cycle
3REJECTEDManager rejected entryReturned for correction

Approval Workflow

code
DRAFT (0) ────────────────> SUBMITTED (1)
                                │
                    ┌───────────┴───────────┐
                    ▼                       ▼
              APPROVED (2)            REJECTED (3)
                    │                       │
                    ▼                       ▼
          Billing Cycle              Back to DRAFT

Workflow Rules:

  • Resources can edit DRAFT entries freely
  • SUBMITTED entries are locked until approved/rejected
  • REJECTED entries return to editable state
  • APPROVED entries are included in next billing cycle
  • Only designated approvers can change status from SUBMITTED

Complete Time Entry Field Reference

Core Fields

FieldTypeRequiredDescription
idintSystemAuto-generated unique identifier
ticketIDintConditionalAssociated ticket (required if no projectID)
projectIDintConditionalAssociated project (required if no ticketID)
taskIDintNoAssociated project task
resourceIDintYesTechnician logging time
dateWorkeddateYesDate work was performed

Time Fields

FieldTypeRequiredDescription
hoursWorkeddecimalYesTotal hours (rounded to quarter-hour)
hoursToBilldecimalNoBillable hours (may differ from worked)
startDateTimedatetimeNoWork start time
endDateTimedatetimeNoWork end time
offsetHoursdecimalNoOffset from actual time

Billing Fields

FieldTypeRequiredDescription
isBillablebooleanNoWhether time is billable
billingCodeIDintNoBilling category code
contractIDintNoAssociated contract
contractServiceIDintNoSpecific service on contract
contractServiceBundleIDintNoService bundle reference
roleIDintNoRole for rate determination

Rate Fields

FieldTypeRequiredDescription
billingRatedecimalNoHourly billing rate
internalCostdecimalNoInternal cost rate
billingAmountdecimalSystemCalculated billing total
costAmountdecimalSystemCalculated cost total

Approval Fields

FieldTypeRequiredDescription
approvalStatusintNoCurrent approval state (0-3)
approvedByResourceIDintSystemWho approved the entry
approvedDateTimedatetimeSystemWhen entry was approved

Description Fields

FieldTypeRequiredDescription
summaryNotestextRecommendedWork summary for client
internalNotestextNoInternal notes (not billed)
nonBillableReasontextConditionalRequired if marking non-billable

Billing Calculations

Rate Hierarchy

Billing rates are determined in this order:

  1. Contract Rate - Specific rate defined in contract
  2. Resource Rate - Rate assigned to technician
  3. Role Rate - Rate based on assigned role
  4. Default Rate - System default rate
javascript
function getBillingRate(timeEntry, context) {
  // Priority 1: Contract rate
  if (context.contractInfo?.hourlyRate) {
    return context.contractInfo.hourlyRate;
  }

  // Priority 2: Resource-specific rate
  if (context.billingRates?.[timeEntry.resourceID]) {
    return context.billingRates[timeEntry.resourceID];
  }

  // Priority 3: Role-based rate
  if (context.billingRates?.[`role_${timeEntry.roleID}`]) {
    return context.billingRates[`role_${timeEntry.roleID}`];
  }

  // Priority 4: Default rate
  return context.defaultRate || 0;
}

Billing Amount Calculation

javascript
function calculateBilling(timeEntry, context) {
  const hours = timeEntry.hoursWorked || 0;
  const isBillable = determineBillability(timeEntry, context);

  if (!isBillable) {
    return { isBillable: false, billingAmount: 0 };
  }

  const billingRate = getBillingRate(timeEntry, context);
  const billingAmount = hours * billingRate;

  // Calculate internal cost
  const costRate = getInternalCostRate(timeEntry, context);
  const costAmount = hours * costRate;

  // Calculate profit metrics
  const markup = costRate > 0 ? ((billingRate - costRate) / costRate) * 100 : 0;
  const profitAmount = billingAmount - costAmount;

  return {
    isBillable,
    billingRate,
    billingAmount: Math.round(billingAmount * 100) / 100,
    costRate,
    costAmount: Math.round(costAmount * 100) / 100,
    markup: Math.round(markup * 100) / 100,
    profitAmount: Math.round(profitAmount * 100) / 100
  };
}

Billability Determination

Time entries are evaluated for billability based on:

ConditionBillable?Reason
Explicit isBillable: trueYesManually marked billable
Explicit isBillable: falseNoManually marked non-billable
Billing code marked non-billableNoBilling code override
Contract excludes T&MNoContract terms
Ticket or project workYesDefault for client work
Internal work (no ticket/project)NoDefault for internal work
javascript
function determineBillability(timeEntry, context) {
  // Explicit setting takes precedence
  if (timeEntry.isBillable !== undefined) {
    return timeEntry.isBillable;
  }

  // Check billing code
  if (timeEntry.billingCodeID && context.billingCodes) {
    const billingCode = context.billingCodes[timeEntry.billingCodeID];
    if (billingCode && !billingCode.isBillable) {
      return false;
    }
  }

  // Check contract terms
  if (context.contractInfo?.includesTimeAndMaterials === false) {
    return false;
  }

  // Default: billable for client work
  return !!(timeEntry.ticketID || timeEntry.projectID);
}

Approval Requirements

Automatic Approval Triggers

Certain conditions automatically require manager approval:

ConditionRequires ApprovalReason
Billable timeYesFinancial impact
Hours > 8YesOvertime review
Weekend workYesPolicy compliance
Holiday workYesPolicy compliance
Exceeds budgetYesCost control
javascript
function requiresApproval(timeEntry, context) {
  // Billable time always requires approval
  if (timeEntry.isBillable) return true;

  // Overtime requires approval
  if (timeEntry.hoursWorked > 8) return true;

  // Weekend work requires approval
  if (timeEntry.dateWorked) {
    const dayOfWeek = new Date(timeEntry.dateWorked).getDay();
    if (dayOfWeek === 0 || dayOfWeek === 6) return true;
  }

  // Budget threshold exceeded
  if (context.projectBudget) {
    const newTotal = context.projectBudget.usedHours + timeEntry.hoursWorked;
    if (newTotal > context.projectBudget.totalHours * 0.9) return true;
  }

  return false;
}

Budget Validation

Project Budget Checks

javascript
function validateProjectBudget(timeEntry, projectBudget) {
  const warnings = [];
  const violations = [];

  const newTotalHours = projectBudget.usedHours + timeEntry.hoursWorked;
  const percentUsed = (newTotalHours / projectBudget.totalHours) * 100;

  // Warning at 90% threshold
  if (percentUsed > 90 && percentUsed <= 100) {
    warnings.push(`Project at ${Math.round(percentUsed)}% of hour budget`);
  }

  // Violation when exceeding budget
  if (percentUsed > 100) {
    violations.push('Time entry exceeds project hour budget');
  }

  return { warnings, violations, percentUsed };
}

Contract Limit Checks

javascript
function validateContractLimits(timeEntry, contractLimits) {
  const warnings = [];
  const violations = [];

  // Check monthly limit
  const newMonthlyHours = contractLimits.usedMonthlyHours + timeEntry.hoursWorked;
  if (newMonthlyHours > contractLimits.monthlyHours) {
    violations.push('Exceeds contract monthly hour limit');
  } else if (newMonthlyHours > contractLimits.monthlyHours * 0.9) {
    warnings.push(`Contract at ${Math.round((newMonthlyHours / contractLimits.monthlyHours) * 100)}% of monthly limit`);
  }

  // Check total contract limit
  const newTotalHours = contractLimits.usedTotalHours + timeEntry.hoursWorked;
  if (newTotalHours > contractLimits.totalHours) {
    violations.push('Exceeds contract total hour limit');
  }

  return { warnings, violations };
}

Time Analytics & KPIs

Utilization Rate Calculation

javascript
function calculateUtilization(timeEntries) {
  let totalHours = 0;
  let billableHours = 0;

  timeEntries.forEach(entry => {
    const hours = entry.hoursWorked || 0;
    totalHours += hours;

    if (entry.isBillable) {
      billableHours += hours;
    }
  });

  const utilizationRate = totalHours > 0
    ? (billableHours / totalHours) * 100
    : 0;

  return {
    totalHours: Math.round(totalHours * 100) / 100,
    billableHours: Math.round(billableHours * 100) / 100,
    nonBillableHours: Math.round((totalHours - billableHours) * 100) / 100,
    utilizationRate: Math.round(utilizationRate * 100) / 100
  };
}

Industry Benchmarks

MetricTargetGoodExcellent
Utilization Rate65%70-75%80%+
Average Daily Hours6.5h7h7.5h
Approval Turnaround24h8h4h
Entry Accuracy95%98%99%+

API Patterns

Creating a Time Entry

http
POST /v1.0/TimeEntries
Content-Type: application/json

Ticket Time Entry:

json
{
  "ticketID": 54321,
  "resourceID": 29744150,
  "dateWorked": "2024-02-15",
  "hoursWorked": 1.5,
  "summaryNotes": "Troubleshot email delivery issues. Identified DNS misconfiguration.",
  "billingCodeID": 12,
  "roleID": 5,
  "isBillable": true
}

Project Time Entry:

json
{
  "projectID": 12345,
  "taskID": 67890,
  "resourceID": 29744150,
  "dateWorked": "2024-02-15",
  "hoursWorked": 4.0,
  "summaryNotes": "Network infrastructure design - Phase 2 planning",
  "internalNotes": "Need to follow up on VLAN configuration",
  "billingCodeID": 8,
  "isBillable": true
}

Query Patterns

Time entries for a ticket:

json
{
  "filter": [
    {"field": "ticketID", "op": "eq", "value": 54321}
  ],
  "includeFields": ["Resource.firstName", "Resource.lastName"]
}

Unapproved time entries for a date range:

json
{
  "filter": [
    {"field": "dateWorked", "op": "between", "value": ["2024-02-01", "2024-02-15"]},
    {"field": "approvalStatus", "op": "in", "value": [0, 1]}
  ]
}

Billable time by resource:

json
{
  "filter": [
    {"field": "resourceID", "op": "eq", "value": 29744150},
    {"field": "isBillable", "op": "eq", "value": true},
    {"field": "dateWorked", "op": "gte", "value": "2024-02-01"}
  ]
}

Submitting for Approval

http
PATCH /v1.0/TimeEntries
Content-Type: application/json
json
{
  "id": 98765,
  "approvalStatus": 1
}

Approving Time Entry

json
{
  "id": 98765,
  "approvalStatus": 2
}

Rejecting Time Entry

json
{
  "id": 98765,
  "approvalStatus": 3,
  "internalNotes": "Please add more detail about the work performed"
}

Business Rules

Quarter-Hour Rounding

Standard MSP practice is to round time to the nearest quarter hour:

javascript
function roundToQuarterHour(hours) {
  return Math.round(hours * 4) / 4;
}

// Examples:
// 1.12 → 1.0
// 1.13 → 1.25
// 1.38 → 1.5
// 1.63 → 1.75
// 1.88 → 2.0

Minimum Billing Increments

Work TypeMinimumRationale
Remote Support0.25h (15 min)Quick remote fixes
Phone Call0.25h (15 min)Brief calls
On-Site Visit1.0h (60 min)Travel overhead
Emergency/After Hours1.0h (60 min)Premium rate

Default Date Handling

If no date is provided, default to the current date:

javascript
function setDefaultDate(timeEntry) {
  if (!timeEntry.dateWorked) {
    timeEntry.dateWorked = new Date().toISOString().split('T')[0];
  }
  return timeEntry;
}

Common Workflows

Daily Time Entry Flow

  1. Log time - Create entry with work details
  2. Review - Check accuracy and completeness
  3. Submit - Change status to SUBMITTED (1)
  4. Await approval - Manager reviews entry
  5. Resolve - Entry approved or rejected

End of Week Timesheet

javascript
// Get all draft entries for the week
const weekEntries = await queryTimeEntries({
  filter: [
    {field: 'resourceID', op: 'eq', value: currentResourceId},
    {field: 'dateWorked', op: 'between', value: [weekStart, weekEnd]},
    {field: 'approvalStatus', op: 'eq', value: 0}
  ]
});

// Submit all for approval
for (const entry of weekEntries) {
  await updateTimeEntry(entry.id, { approvalStatus: 1 });
}

Manager Approval Queue

javascript
// Get pending approvals for my team
const pendingApprovals = await queryTimeEntries({
  filter: [
    {field: 'approvalStatus', op: 'eq', value: 1},
    {field: 'dateWorked', op: 'gte', value: lastWeekStart}
  ],
  includeFields: ['Resource.firstName', 'Resource.lastName', 'Ticket.title']
});

Error Handling

Common API Errors

CodeMessageResolution
400TicketID or ProjectID requiredProvide either ticket or project
400Invalid hours valueHours must be positive decimal
400Future date not allowedDate cannot be in future
401UnauthorizedVerify API credentials
403Cannot modify approved entryEntry is locked after approval
409Entry already submittedCannot edit while pending

Validation Errors

ErrorCauseFix
ResourceID requiredMissing technicianAdd resourceID field
Invalid dateWorkedDate format wrongUse YYYY-MM-DD format
Hours exceed 24Too many hoursCheck hour calculation
Missing summaryNo descriptionAdd summaryNotes

Best Practices

  1. Log time immediately - Don't batch at end of day; details get lost
  2. Use descriptive summaries - Clients see these on invoices
  3. Round appropriately - Follow minimum billing rules
  4. Validate before submitting - Check accuracy before approval
  5. Link to tickets/projects - Always associate with work items
  6. Monitor utilization - Track billable vs non-billable ratio
  7. Review budget warnings - Address before exceeding limits
  8. Use billing codes - Categorize time for reporting
  9. Keep internal notes separate - Don't bill clients for non-value work
  10. Approve promptly - Long approval queues delay billing

Related Skills