Labor Items
Overview
Labor items represent crew members with automatic calculation of soft costs (payroll taxes) and auto costs (vehicle depreciation, fuel, insurance). The system also auto-calculates dependent roles like Operations Coordinator and Labor Foreman.
Endpoints
List Labor Items
- Request
- Response
GET /api/labor/scope/:scopeId
Cookie: sAccessToken=...; sRefreshToken=...
[
{
"id": "uuid",
"scopeId": "uuid",
"role": "Skilled Laborer",
"quantity": 4,
"hoursPerDay": 8,
"ratePerHour": 25.00,
"days": 5,
"dollarPerDay": 800.00,
"totalCost": 5240.00,
"autoCosts": {
"depreciation": 120.00,
"fuel": 80.00,
"insurance": 40.00
},
"softCosts": {
"fica": 306.00,
"futa": 24.00,
"suta": 70.00
}
},
...
]
Roles: ADMIN, ESTIMATOR, PM
Create Labor Item
- Request
- Response
POST /api/labor
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"scopeId": "uuid",
"role": "Skilled Laborer",
"quantity": 4,
"hoursPerDay": 8,
"ratePerHour": 25.00,
"days": 5
}
{
"id": "uuid",
"dollarPerDay": 800.00,
"totalCost": 5240.00,
"message": "Labor item created successfully"
}
Roles: ADMIN, ESTIMATOR
Required Fields:
scopeId(string, UUID) - Parent scoperole(string) - Job role (e.g., "Skilled Laborer", "Operator")quantity(number) - Number of workershoursPerDay(number) - Hours worked per dayratePerHour(number) - Hourly wagedays(number) - Duration in days
Update Labor Item
- Request
- Response
PUT /api/labor/:id
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"quantity": 6,
"days": 7
}
{
"id": "uuid",
"totalCost": 7336.00,
"message": "Labor item updated successfully"
}
Roles: ADMIN, ESTIMATOR
Delete Labor Item
- Request
- Response
DELETE /api/labor/:id
Cookie: sAccessToken=...; sRefreshToken=...
{
"message": "Labor item deleted successfully"
}
Roles: ADMIN, ESTIMATOR
Automatic Calculations
Base Cost
dollarPerDay = quantity × hoursPerDay × ratePerHour
baseCost = dollarPerDay × days
Soft Costs (Payroll Taxes)
ficaRate = 0.0765 // 7.65% (Social Security + Medicare)
futaRate = 0.006 // 0.6% (Federal Unemployment)
sutaRate = 0.0175 // 1.75% (State Unemployment)
ficaAmount = baseCost × ficaRate
futaAmount = baseCost × futaRate
sutaAmount = baseCost × sutaRate
Auto Costs (Vehicle Expenses)
For roles that require company vehicles:
autoDepreciationRate = 0.03 // 3%
autoFuelRate = 0.02 // 2%
autoInsuranceRate = 0.01 // 1%
autoDepreciation = baseCost × autoDepreciationRate
autoFuel = baseCost × autoFuelRate
autoInsurance = baseCost × autoInsuranceRate
autoCosts = autoDepreciation + autoFuel + autoInsurance
Total Cost
totalCost = baseCost + fica + futa + suta + autoCosts
Dependent Roles (Read-Only)
Certain roles are automatically calculated based on skilled labor hours. These appear in the UI but cannot be directly edited.
Operations Coordinator
skilledLaborHours = Σ (quantity × hoursPerDay × days) for "Skilled Laborer" role
opsCoordinatorHours = Math.min(skilledLaborHours × 0.05, 40)
- 5% of skilled labor hours
- Capped at 40 hours
Labor Foreman
totalLaborers = Σ quantity for all labor items
laborForemanCount = Math.ceil(totalLaborers / 10)
- 1 foreman per 10 laborers
Junior Foreman
totalLaborers = Σ quantity for all labor items
juniorForemanCount = Math.ceil(totalLaborers / 5)
- 1 junior foreman per 5 laborers
Data Model
interface LaborItem {
id: string // UUID
scopeId: string // Parent scope UUID
role: string // Job role
quantity: number // Number of workers
hoursPerDay: number // Daily hours
ratePerHour: number // Hourly wage
days: number // Duration
// Calculated Values (read-only)
dollarPerDay: number
totalCost: number
autoCosts: {
depreciation: number
fuel: number
insurance: number
} | null
softCosts: {
fica: number
futa: number
suta: number
}
// Relations
scope: Scope
}
Common Labor Roles
- Skilled Laborer - General construction worker
- Operator - Heavy equipment operator
- Carpenter - Framing/formwork specialist
- Concrete Finisher - Concrete finishing specialist
- Foreman - Crew supervisor
- Junior Foreman - Assistant supervisor
- Operations Coordinator - Project coordination (auto-calculated)
Example: 4-Person Crew for 5 Days
{
"role": "Skilled Laborer",
"quantity": 4,
"hoursPerDay": 8,
"ratePerHour": 25.00,
"days": 5
}
Calculation:
- Base: 4 × 8 × $25 × 5 = $4,000
- FICA (7.65%): $306
- FUTA (0.6%): $24
- SUTA (1.75%): $70
- Auto Costs (6%): $240
- Total: $4,640
Gantt Mode
When bid.laborBiddingMode = "GANTT", labor is tracked at bid level with timeline-based allocation. Labor types are placed on a day-based timeline and costs distribute automatically to overlapping scopes.
List Bid Labor
- Request
- Response
GET /api/bids/:bidId/bid-labor
Cookie: sAccessToken=...; sRefreshToken=...
[
{
"id": "uuid",
"bidId": "uuid",
"laborType": "Skilled Labor",
"quantity": 4,
"hoursPerDay": 8,
"ratePerHour": 27.63,
"startDay": 3,
"durationDays": 10,
"totalCost": 8841.60,
"distributions": [
{
"scopeId": "uuid",
"scopeName": "Foundation",
"allocatedDays": 5,
"percentage": 50.0,
"allocatedCost": 4420.80
},
{
"scopeId": "uuid",
"scopeName": "Grade Beam",
"allocatedDays": 5,
"percentage": 50.0,
"allocatedCost": 4420.80
}
]
},
...
]
Roles: ADMIN, ESTIMATOR, PM
Create Gantt Labor
- Request
- Response
POST /api/bids/:bidId/bid-labor
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"laborType": "Skilled Labor",
"quantity": 4,
"hoursPerDay": 8,
"ratePerHour": 27.63,
"startDay": 3,
"durationDays": 10
}
{
"id": "uuid",
"totalCost": 8841.60,
"message": "Labor created successfully"
}
Roles: ADMIN, ESTIMATOR
Required Fields:
laborType(string) - Labor role namequantity(number) - Number of workershoursPerDay(number) - Hours per worker per dayratePerHour(number) - Hourly wagestartDay(number) - Start day (1-based)durationDays(number) - Duration in days
Update Gantt Labor
- Request
- Response
PATCH /api/bids/:bidId/bid-labor/:laborId
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"startDay": 5,
"durationDays": 8,
"quantity": 6
}
{
"id": "uuid",
"totalCost": 10609.92,
"message": "Labor updated successfully"
}
Roles: ADMIN, ESTIMATOR
Delete Gantt Labor
- Request
- Response
DELETE /api/bids/:bidId/bid-labor/:laborId
Cookie: sAccessToken=...; sRefreshToken=...
{
"message": "Labor deleted successfully"
}
Roles: ADMIN, ESTIMATOR
Batch Update Labor
- Request
- Response
POST /api/bids/:bidId/bid-labor/batch
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"create": [
{
"laborType": "Operator",
"quantity": 2,
"hoursPerDay": 8,
"ratePerHour": 29.84,
"startDay": 1,
"durationDays": 5
}
],
"update": [
{
"id": "uuid",
"startDay": 3,
"durationDays": 8
}
],
"delete": ["uuid1", "uuid2"]
}
{
"created": 1,
"updated": 1,
"deleted": 2,
"message": "Batch update completed"
}
Roles: ADMIN, ESTIMATOR
Switch Labor Bidding Mode
- Request
- Response
PATCH /api/bids/:bidId/bid-labor/mode
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"laborBiddingMode": "GANTT"
}
{
"message": "Labor bidding mode updated to GANTT",
"warning": "This will clear existing labor items"
}
Roles: ADMIN, ESTIMATOR
Switching modes will clear all existing labor items for the selected mode. Use /mode/confirm to proceed.
Confirm Labor Mode Switch
- Request
- Response
POST /api/bids/:bidId/bid-labor/mode/confirm
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"laborBiddingMode": "GANTT"
}
{
"message": "Labor bidding mode switched to GANTT"
}
Roles: ADMIN, ESTIMATOR
Get Labor Distributions
- Request
- Response
GET /api/bids/:bidId/bid-labor/distributions
Cookie: sAccessToken=...; sRefreshToken=...
[
{
"laborId": "uuid",
"laborType": "Skilled Labor",
"totalCost": 8841.60,
"distributions": [
{
"scopeId": "uuid",
"scopeName": "Foundation",
"allocatedDays": 5,
"percentage": 50.0,
"allocatedCost": 4420.80
},
...
]
},
...
]
Roles: ADMIN, ESTIMATOR, PM
Get Scope Labor Allocations
- Request
- Response
GET /api/bids/:bidId/bid-labor/scope/:scopeId
Cookie: sAccessToken=...; sRefreshToken=...
[
{
"laborId": "uuid",
"laborType": "Skilled Labor",
"allocatedDays": 5,
"percentage": 50.0,
"allocatedCost": 4420.80
},
...
]
Roles: ADMIN, ESTIMATOR, PM
Get Labor Coverage Details
- Request
- Response
GET /api/bids/:bidId/bid-labor/:laborId/coverage
Cookie: sAccessToken=...; sRefreshToken=...
{
"labor": {
"id": "uuid",
"laborType": "Skilled Labor",
"startDay": 3,
"durationDays": 10,
"totalCost": 8841.60
},
"scopes": [
{
"scopeId": "uuid",
"scopeName": "Foundation",
"allocatedDays": 5,
"percentage": 50.0,
"allocatedCost": 4420.80
},
...
]
}
Roles: ADMIN, ESTIMATOR, PM
Recalculate Labor Distributions
- Request
- Response
POST /api/bids/:bidId/bid-labor/recalculate
Cookie: sAccessToken=...; sRefreshToken=...
{
"message": "Labor distributions recalculated",
"totalLabor": 3,
"totalScopes": 4
}
Roles: ADMIN, ESTIMATOR
Gantt Mode Cost Calculations
// Base cost
totalCost = quantity × hoursPerDay × ratePerHour × durationDays
// Distribute across scopes based on timeline overlap
for each scope {
overlapDays = calculateOverlap(labor.startDay, labor.durationDays, scope.timeline)
percentage = overlapDays / totalOverlapDays
allocatedCost = totalCost × percentage
}
Gantt Mode Data Model
interface BidLabor {
id: string
bidId: string
laborType: string
quantity: number
hoursPerDay: number
ratePerHour: number
startDay: number
durationDays: number
totalCost: number
distributions: LaborDistribution[]
}
interface LaborDistribution {
scopeId: string
scopeName: string
allocatedDays: number
percentage: number
allocatedCost: number
}
Related Endpoints
- Scopes
- Global Variables - Configure soft cost rates
- Cost Rollup
- Timeline Bidding Guide - User guide for timeline mode
- Equipment Items - Equipment endpoints (Traditional + Gantt)