VG Cost Code, Segment & Work Order System
Overview
VG's business-wide system has THREE layers:
| Layer | What it is | Lives at | Used by |
|---|---|---|---|
| Cost Code | Atomic line-item category (e.g., 52.1 Framing Materials) |
Cost Code Unified.csv |
QB Products/Services |
| Segment | Accounting category (e.g., 52 Framing) |
segments.yaml + cost-code-overrides.yaml |
budgets; estimates; actualsboard matrix |
| Work Order | Operational packet a PM issues to a contractor (e.g., P1WO6 Framing) |
work-orders.yaml |
QB sub-customers (Customer:Project); PM scheduling; contractor docs |
Flow: Cost Code → (exactly one) Segment. Work Order is a separate operational packet that a PM issues to a contractor; vendors post expenses to a WO sub-customer in QB. The WO-to-Segment relationship is 1:1, but cost codes are shared across WOs within the same Segment (e.g., 71.1 Tile Materials appears on both the Backsplash WO and the Tile Installation WO).
Phase (P0/P1/P2/P3/P4) is a Work Order scheduling concept only. It does NOT affect Segment categorization.
Segments
39 total: 32 construction + 7 non-construction.
Construction (32):
16 Project Prep, 18 Site Management, 20 Site Systems, 32 Demolition, 42 Foundation, 44 Site Work, 52 Framing, 53 Roofing, 54 Windows & Exterior Doors & Garage, 56 Interior Stairs, 58 Siding & Shutters, 62 HVAC, 63 Plumbing, 64 Electrical, 65 Security & Technology, 66 Fireplaces, 67 Insulation & Soundproofing, 69 Drywall, 71 Tile, 72 Flooring, 73 Cabinetry & Countertops, 74 Interior Carpentry, 75 Painting Wallpaper & Stain, 76 Accessories, 77 Appliances, 82 Exterior Carpentry, 84 Exterior Masonry, 85 Exterior Painting, 86 Fencing, 88 Landscape & Grounds, 92 Punch List, 94 Other Expenses-Con
Non-construction (7):
12 Buying, 13 Financing, 14 Holding Costs, 93 Sale Preparation, 95 Other Expenses-NonCon, 96 Selling, 99 Sale
Canonical registry: segments.yaml.
Cost Code -> Segment Resolution
Default: leading 2 digits of the cost code is the segment code, IF that segment exists.
Overrides (cost-code-overrides.yaml):
by_code (specific code -> segment):
30.0 -> 44; 40.0 -> 44; 50.0 -> 52; 60.0/70.0/80.0 -> 94; 72.8 -> 74; 90.0 -> 93; 92.0/92.7/92.8/92.9 -> 93; 92.4 -> 92; 96.1/96.8 -> 99.
by_prefix (prefix range -> segment):
22/24/26/28/29 -> 20 (Site Systems rollup); 68 -> 67 (Sound Proofing merged into Insulation & Soundproofing).
Order: by_code, by_prefix, default 2-digit, otherwise unmapped.
Work Orders
39 operational packets across phases P1/P2/P3. P0/P4 have no WOs (they're financial). Canonical registry: work-orders.yaml.
1:1 invariant: every WO's cost_codes resolve to the same Segment. PMs can add/subtract WOs from a Segment budget unambiguously.
Not every cost code is on a WO. Band-parent codes like 30.0, 60.0, 70.0, 80.0 resolve to a Segment but typically appear on no WO packet — they're catch-alls for miscoded/ad-hoc expenses.
Cost code can appear on multiple WOs (e.g., 74.1 is on both P3WO5 Interior Doors and P3WO13 Hardware). Both resolve to Segment 74; PM picks the right WO at expense time.
Field manager is assigned per WO at issue time by the PM. Not pre-populated.
Files
| File | Role |
|---|---|
segments.yaml |
Segment registry (39) |
cost-code-overrides.yaml |
Cost code -> segment exceptions |
work-orders.yaml |
Operational WO roster |
Cost Code Unified.csv |
Per-cost-code table with derived Segment + Operational WOs columns |
phases.yaml |
Phase definitions (P0–P4) — used by Work Orders only |
Adding a new cost code
- Add a row to
Cost Code Unified.csv - If the leading 2 digits aren't a Segment in
segments.yaml, add an entry tocost-code-overrides.yaml - If the code should appear on a WO, add it to that WO's
cost_codeslist inwork-orders.yaml - Run
python3 tools/regenerate_cost_code_csv.pyto refresh derived columns - Run
python3 -m pytest tools/test_*.pyto verify invariants
QB Sub-customer Architecture
Work Orders are QB sub-customers (Customer:Project) under the property parent customer. Segments are derived automatically from cost codes and are NOT separate QB projects.
<Property Customer> ← QB Customer
├── P1WO6 Framing ← QB Sub-customer (Work Order)
├── P1WO7 Roofing
└── ... (43 WOs total)
When a vendor submits expenses, they tag them to a WO sub-customer. The actualsboard reads this from the QB transaction's CustomerRef field (raw_customer_ref) to populate the WO actuals section.
Provisioning: tools/qb_replace_segments_with_wos.py — replaces Segment sub-customers with WO sub-customers on a given property. Run --dry-run first, then --apply to confirm with the user. Line items categorized via tools/qb_categorize_lines.py.