Skip to content

Excel Imports

StatusShipped
LinearPIN-295 ↗ PIN-448 ↗
ModuleSmart Import ↗

Two places in the app parse spreadsheets into data rows: the Takeoff grid on a bid (Excel/CSV → takeoff quantity rows, parsed in the browser) and the Submittal log on a project (xlsx/CSV → submittal rows, parsed server-side, all-or-nothing). Everywhere else an .xlsx is just stored as a document — uploading one to a Documents folder does not create data.

Prerequisites

  • Role: Estimator or Chief Estimator for the takeoff import; PM for submittals.
  • Bid state: a bid with an Estimate created (open the Estimate tab → Create Estimate if none).
  • Test data: estimates-xlsx/E25-1474 WNET 437 Madison Ave 3rd Fl.xlsx (duct) or E25-1500 Netflix 888 Broadway 3rd Fl.xlsx (pipe) from the test-app-files Drive folder. HVAC Bids Log.xlsx works as a second sheet-picking exercise.
  • Permissions cached? Relogin if the Import button is missing.

1. Takeoff import — open the modal

Open a bid → Estimate tab → Takeoff sub-tab. In the toolbar, click Import (spreadsheet icon, tooltip "Import from Excel or CSV").

The modal Import Takeoff from Excel / CSV accepts .xlsx, .xlsm, .csv. Parsing happens entirely in the browser — nothing uploads until you confirm.

2. Pick a sheet and map columns

Select the file from estimates-xlsx/. The modal lets you:

  1. Pick a sheet — real estimate workbooks have several; try a non-first sheet (the E25-1474 workbook has 10, Duct Takeoff is a good target).
  2. Map columns — match spreadsheet columns to takeoff fields (Skip / Trade / Description / System / Size / Material / Quantity / Unit / Unit Cost / Spec Name / CSI Code / Notes / Drawing Ref).
  3. Choose mode — append new rows vs update existing.

⚠ A Trade (or System) column mapping is effectively required — verified live 2026-07-08: with no Trade mapped, Preview says "N valid" but the backend rejects every row (400) and the modal closes with zero rows and no error message. Silent-failure bug worth a ticket; until fixed, always map Trade.

Confirm. Rows land in the takeoff grid in batches (large files import in chunks of 500 — a many-hundred-row sheet is a good stress test).

3. Verify the takeoff grid

Spot-check a handful of imported rows against the spreadsheet: quantities, descriptions, no shifted columns. Re-import the same file in update mode and confirm it doesn't duplicate rows.

⚠ The import fills the takeoff quantity grid only — it does not create Estimate scope line items. Estimate rows are entered manually (or AI-assisted) in the Estimate grid. "I imported Excel but the Estimate is empty" is intended behavior, not a bug.

4. Submittal log import (project-side)

Open a project → Submittals tab → Import Submittals. Accepts a full submittal-log .xlsx (vendor, dates, status, lead time, shipping mapped) or a simple .csv / pasted rows.

  • Parsing is server-side and all-or-nothing: one bad row rejects the whole file — verify a clear error message says why.
  • There's no sample submittal log in the Drive folder yet — for a smoke test, paste 2–3 rows or make a minimal CSV (ask me for the expected columns).

Verification Checklist

  • Import button is visible in the Takeoff toolbar for an Estimator
  • Modal reads the xlsx in-browser, lists all sheets, and lets you map columns
  • Imported row values match the source spreadsheet (no off-by-one column shifts)
  • Re-import in update mode does not duplicate rows
  • Submittal import: valid rows all land; an invalid file is rejected whole with a readable error
  • No console errors during either import

Known Issues / Current Gaps

  • ⚠ No xlsx bid import exists — bids come from manual creation, BuildingConnected/Procore sync, or SharePoint import (see doc 02, Priority 10). Don't test "import HVAC Bids Log as bids".
  • 🕐 No takeoff→estimate auto-promotion from the import path — estimate lines stay manual/AI-assisted.

Updating this walkthrough

Run the full flow yourself end-to-end. If it passes, bump last_verified: in the frontmatter to today's date. If any step fails, fix the doc or the code in the same PR — drift is the enemy.