Skip to main content

Material Items

Overview

Material items represent construction materials (lumber, PPE, tools, etc.) with configurable waste factors and automatic tax calculation.

info

Materials auto-generated by concrete items (concrete mix, rebar) are excluded from cost rollup to prevent double-counting (BM-32).

Endpoints

List Material Items

GET /api/materials/scope/:scopeId
Cookie: sAccessToken=...; sRefreshToken=...

Roles: ADMIN, ESTIMATOR, PM


Create Material Item

POST /api/materials
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"scopeId": "uuid",
"materialType": "Lumber",
"quantity": 100,
"wastePercent": 10.0,
"unit": "LF",
"pricingItemId": "uuid"
}

Roles: ADMIN, ESTIMATOR

Required Fields:

  • scopeId (string, UUID) - Parent scope
  • materialType (string) - Material name/description
  • quantity (number) - Base quantity
  • unit (string) - Unit of measure (LF, SF, EA, etc.)
  • pricingItemId (string, UUID) - Material pricing reference

Optional Fields:

  • wastePercent (number, default: 0) - Waste factor percentage
  • sourceConcreteItemId (string, UUID) - If auto-generated from concrete item

Update Material Item

PUT /api/materials/:id
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"quantity": 150,
"wastePercent": 15.0
}

Roles: ADMIN, ESTIMATOR


Delete Material Item

DELETE /api/materials/:id
Cookie: sAccessToken=...; sRefreshToken=...

Roles: ADMIN, ESTIMATOR

warning

Cannot delete material items auto-generated from concrete items (sourceConcreteItemId not null). Delete the concrete item instead.

Cost Calculation

// Waste calculation
adjustedQuantity = quantity × (1 + wastePercent/100)

// Base cost
baseCost = adjustedQuantity × unitCost

// Tax (if not tax exempt)
taxAmount = taxExempt ? 0 : baseCost × taxRate

// Total
totalCost = baseCost + taxAmount

Waste Factors

Common waste percentages by material type:

Material TypeTypical Waste
Lumber10-15%
Concrete Accessories5-10%
PPE0%
Tools0%
Safety Equipment0%

Auto-Generated Materials

When a concrete item is created, two material items are automatically generated:

  1. Concrete Mix - Cubic yards → pricing
  2. Rebar - Pounds → pricing

These items have sourceConcreteItemId set and are:

  • Read-only (edit the concrete item instead)
  • Excluded from material cost rollup (already counted in concrete cost)
  • Displayed in UI with special indicator
info

This prevents double-counting concrete costs while maintaining a complete material list.

Material Categories

Materials are filtered by scope type via scope type material categories (BM-39).

Missing Categories

If a material's category is not in the scope type's allowed categories, it displays a "missing category" chip in the UI.

Example:

  • Scope type: "Slab"
  • Allowed categories: ["PPE", "Lumber", "Safety"]
  • Material: "Concrete Accessories" (not in allowed list)
  • Result: ⚠️ Warning chip displayed

View Scope Type Material Categories →

Data Model

interface MaterialItem {
id: string // UUID
scopeId: string // Parent scope UUID
materialType: string // Material name
quantity: number // Base quantity
wastePercent: number // Waste factor (%)
adjustedQuantity: number // With waste applied (read-only)
unit: string // Unit of measure
unitCost: number // Price per unit (read-only, from pricing)
baseCost: number // Before tax (read-only)
taxAmount: number // Tax amount (read-only)
totalCost: number // Final cost (read-only)
pricingItemId: string // Pricing reference
sourceConcreteItemId: string | null // If auto-generated

// Relations
scope: Scope
pricingItem: PricingItem
sourceConcreteItem: ConcreteItem | null
}

Units of Measure

Common units:

  • LF - Linear feet
  • SF - Square feet
  • EA - Each (pieces)
  • BOX - Box
  • GAL - Gallons
  • LBS - Pounds
  • TON - Tons

Tax Exemption

Tax calculation respects the Bid.taxExempt flag:

  • taxExempt = false → Tax applied
  • taxExempt = true → No tax

Example Calculation

{
"materialType": "Lumber 2x4x8",
"quantity": 100,
"wastePercent": 10,
"unitCost": 5.50,
"taxRate": 0.0825
}

Calculation:

  • Adjusted quantity: 100 × 1.10 = 110 LF
  • Base cost: 110 × $5.50 = $605.00
  • Tax: $605.00 × 0.0825 = $49.91
  • Total: $654.91