Material Items
Overview
Material items represent construction materials (lumber, PPE, tools, etc.) with configurable waste factors and automatic tax calculation.
Materials auto-generated by concrete items (concrete mix, rebar) are excluded from cost rollup to prevent double-counting (BM-32).
Endpoints
List Material Items
- Request
- Response
GET /api/materials/scope/:scopeId
Cookie: sAccessToken=...; sRefreshToken=...
[
{
"id": "uuid",
"scopeId": "uuid",
"materialType": "Lumber",
"quantity": 100,
"wastePercent": 10.0,
"adjustedQuantity": 110,
"unit": "LF",
"unitCost": 5.50,
"baseCost": 605.00,
"taxAmount": 49.91,
"totalCost": 654.91,
"pricingItemId": "uuid",
"sourceConcreteItemId": null
},
...
]
Roles: ADMIN, ESTIMATOR, PM
Create Material Item
- Request
- Response
POST /api/materials
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"scopeId": "uuid",
"materialType": "Lumber",
"quantity": 100,
"wastePercent": 10.0,
"unit": "LF",
"pricingItemId": "uuid"
}
{
"id": "uuid",
"adjustedQuantity": 110,
"totalCost": 654.91,
"message": "Material item created successfully"
}
Roles: ADMIN, ESTIMATOR
Required Fields:
scopeId(string, UUID) - Parent scopematerialType(string) - Material name/descriptionquantity(number) - Base quantityunit(string) - Unit of measure (LF, SF, EA, etc.)pricingItemId(string, UUID) - Material pricing reference
Optional Fields:
wastePercent(number, default: 0) - Waste factor percentagesourceConcreteItemId(string, UUID) - If auto-generated from concrete item
Update Material Item
- Request
- Response
PUT /api/materials/:id
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json
{
"quantity": 150,
"wastePercent": 15.0
}
{
"id": "uuid",
"adjustedQuantity": 172.5,
"totalCost": 1056.38,
"message": "Material item updated successfully"
}
Roles: ADMIN, ESTIMATOR
Delete Material Item
- Request
- Response
DELETE /api/materials/:id
Cookie: sAccessToken=...; sRefreshToken=...
{
"message": "Material item deleted successfully"
}
Roles: ADMIN, ESTIMATOR
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 Type | Typical Waste |
|---|---|
| Lumber | 10-15% |
| Concrete Accessories | 5-10% |
| PPE | 0% |
| Tools | 0% |
| Safety Equipment | 0% |
Auto-Generated Materials
When a concrete item is created, two material items are automatically generated:
- Concrete Mix - Cubic yards → pricing
- 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
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 appliedtaxExempt = 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
Related Endpoints
- Scopes
- Concrete Items - Auto-generates materials
- Scope Types
- Pricing Items
- Cost Rollup