Migrate from WinQS to QSNAPP
In-depth guide to importing a WinQS project library (.WCLIB) into QSNAPP — grounded in confirmed schema behaviour from real restored project databases, not the empty staging tables the schema appears to advertise.
Overview
WinQS stores a full quantity-surveying project as a SQL Server database packaged inside a .WCLIB archive. QSNAPP can ingest that library and map the real QS artefacts your practice already uses: elemental Bills of Estimates (BoE), trade Bills of Quantities (BoQ), DimX takeoff batches, variation orders, and JBCC certificate history.
This guide documents the confirmed import path — reverse-engineered from restored project databases (including a full measured project with DimX batches, BoE, BoQ, issued certificates, VOs, and recovery statements). Where the schema and real behaviour disagree, this guide follows the data.
Source of truth
Findings here align with QSNAPP's qs-brain vault (knowledge/qs-brain) and the WinQS import forward plan (knowledge/WINQS_IMPORT_FORWARD.md). Treat claims as confirmed only when they cite real tables, row behaviour, or workflow steps from that research.
Prerequisites
- A WinQS project export as
.WCLIB(or an extracted.bak/ CSV fixture pack for testing). - A QSNAPP project with permission to run imports.
- A clear first import slice (DimX-only, BoQ, BoE, or certificates) so you are not forced to migrate the entire lifecycle on day one.
- Awareness of the guardrails — especially decoy fields like
item.vono.
What a .WCLIB contains
A .WCLIB file is a 7z archive, not a proprietary opaque blob. Inside you will typically find a SQL Server backup (.bak) that is the actual project database.
.WCLIB (7z archive)
└── <project>.bak # SQL Server database backup
└── 78+ tables / views # structure, item, dimension, certificates, …Confirmed research databases used to validate this guide include a full measured construction project (BoE, BoQ, DimX, multiple JBCC certificates, variations, recoveries) and a preliminaries-only contrast case. The import path does not require shipping SQL Server into the QSNAPP production runtime — ETL / offline restore / fixture packs can feed the mapper.
Import pipeline
End-to-end flow from library file to QSNAPP entities:
- Ingest — accept the
.WCLIB, unpack with 7z, locate the.bak. - Materialise — restore offline (lab) or load from pre-exported CSV fixtures for the tables you need.
- Classify by doctype — walk
structurefiltered bydoctype(E/Q/X/G/J). - Map the measurement chain —
structure → item → dimension → itemloc → itemprice. - Attach provenance — DimX batches via
extimport/extimportlink. - Validate against guardrails — refuse decoy sources; verify quantities and certificate math on a sample before full cutover.
# Conceptual pipeline (lab / ETL — not the Next.js request path)
unpack(.wclib)
→ find(.bak)
→ materialise(tables)
→ map(structure[doctype], item, dimension, itemloc, itemprice)
→ link(extimport → extimportlink → item)
→ validate(guardrails)
→ write(QSNAPP project schema)Document types (structure.doctype)
WinQS reuses the same measurement tables for different documents. The split is structure.doctype. Never merge these into one bill without an explicit re-home step.
| Doctype | Document | QSNAPP intent |
|---|---|---|
E | Bill of Estimates (elemental cost plan) | Map to elemental estimating / AAQS-aligned sections |
Q | Bill of Quantities (trade-measured contract bill) | Map to BoQ structures; keep Model Preambles / H1–H4 / TX rows |
X | DimX / DimensionX takeoff landing zone | Import as measured, unpriced lines; price later |
G | General-purpose constants (GPC structure) | Import named constants as reusable factors — not via empty gpconstant |
J | JBCC / contract-admin related structure | Bring in with certificate / valuation slices |
The measurement chain
Every measured item — manual or DimX — ends in the same doctype-agnostic chain. This is the load-bearing abstraction for the importer:
structure # bill / section tree (parentstructureno)
→ item # heading (H1–H4), text (TX), or measured line
→ dimension # takeoff: dimqty1-3, dimexpr, dimtotal (+ vono for VOs)
→ itemloc # item × location → measqty / valqty (ties to dimtotal)
→ itemprice # rate from price / pricegroup (may be empty on DimX)- Headings and preambles are items too —
unittypewithmeasflag=0(H1–H4,TX,FF,ST) sits interleaved with measured rows. - Measured units use
measflag=1types such asM,M2,M3,NO,T,ITEM,%IT(percentage-of-items / builder's work allowance). - Secondary classification via
keytype→keydesc→itemkeyis independent of structure placement. Do not assume setting one sets the other.
DimX (DimensionX) imports — how they really work
Critical: ignore the empty staging table
SpoormakerDimXItems looks purpose-built for BIM/takeoff import (and stored procedures reference it), but on confirmed real projects it can be completely empty. The live path bypasses it.
Confirmed real behaviour:
- Items are created directly on
itemwithcreateuser = 'dimX'(a sentinel, not a human login), often in a sub-second timestamp burst. - They land under a dedicated structure:
doctype='X',structuretype='B', description along the lines ofdimensionX. - Batch metadata lives in
extimport(filename, import date, named operator, revision, Merged flag, project name). Links useextimportlink.winqslinkno = item.itemno. - Imported items are measured but not priced —
itemloc/dimensionpopulated;itempriceempty. Treat rate application as a separate follow-up in QSNAPP. item.vono = -1on DimX items is a DimX sentinel, not a variation reference.
After import into QSNAPP you must still decide the document target when leaving the DimX zone:
- Early design / composite costs → re-home into BoE (E) using AAQS element naming.
- Tender / contract detail → re-home into BoQ (Q) using Standard System / Model Bills trade order.
BoE vs BoQ — keep them separate
Both share the measurement chain; they are different documents. Confirmed practice on real WinQS projects:
| Aspect | BoE (E) | BoQ (Q) |
|---|---|---|
| Purpose | Elemental cost planning | Trade-measured contract / tender bill |
| Section naming | AAQS element names (by name, not numeric codes) | Standard System / Model Bills trades |
| Preambles | Generally absent / elemental composite wording | Model Preambles for Trades + SUPPLEMENTARY PREAMBLES (TX / H2 / H4) |
| Typical content | Composite / provisional costs early on | Detailed measured items per trade |
Do not mis-attribute
Unscoped queries that ignore doctype will mix BoQ preamble text into BoE results. Always filter by doctype before mapping into QSNAPP modules.
Choose an import slice
You do not need a big-bang migration. Pick the slice that unlocks the next workflow, then expand.
| Slice | Primary tables | Outcome in QSNAPP |
|---|---|---|
| DimX measured items | structure(X), item, dimension, itemloc, extimport* | Takeoff lines ready for pricing / re-home |
| BoQ structure + items | structure(Q), item, dimension, itemloc, itemprice, unittype | Trade bill with headings and rates |
| Elemental BoE | structure(E), item, … (+ constants / %IT) | AAQS-aligned cost plan |
| Certificates & VOs | variation, dimension.vono, itemlochistory, ccrcert, … | JBCC valuation / payment history |
Field mapping (WinQS → QSNAPP concepts)
| WinQS | Maps to | Notes |
|---|---|---|
| structure + parentstructureno | Bill / estimate section tree | Filter by doctype first |
| item + unittype | Line (heading, text, or measured) | Preserve H1–H4 / TX order |
| dimension | Takeoff / measurement rows | VO tag = dimension.vono |
| itemloc | Location quantities | measqty should tie to dimtotal |
| itemprice / price / pricegroup | Rates | Often empty on DimX imports |
| extimport / extimportlink | Import batch provenance | Prefer over SpoormakerDimXItems |
| keydesc / itemkey | Secondary trade / activity tags | Independent of structure |
| variation | Variation order register | Not AuthorisedVariations alone |
| itemlochistory | Certified snapshots | Not CalculatedItemHistory |
| ccrcert / ContractorCertificates | Certificate values & payments | Pay the marginal amount |
Guardrails — do not trust these
Copy this checklist into any importer review. Every entry is confirmed against real project data.
| Do not use as source of truth | Use instead | Why |
|---|---|---|
item.vono | dimension.vono | DimX sentinel (often -1), not VO tracking |
SpoormakerDimXItems | item + extimport* | Staging table can be unused entirely |
CalculatedItemHistory | itemlochistory | Large row counts can be empty scaffolding |
Contractors | contractor → Persons | Legacy capitalised table often empty |
gpconstant | Items under doctype G structure | Real GP constants live as bill items |
AuthorisedVariations | variation + dimension.vono | Manual override, not full VO register |
ccrcert.certvalue alone | Marginal payment (vs previous) | certvalue is cumulative |
ccrcertno = 0 | Filter out | Synthetic pre-certificate baseline |
Variation orders
To migrate VOs correctly: create / import the variation register, then ensure every affected measured row carries dimension.vono for that variation.
- Finding what variation N changed → filter
dimensiononvono, neveritem.vono. - Financial impact signature → growing gap between
ccrcert.contractvalue(original sum) andccrcert.efcvalue(estimated final cost). - A VO record existing does not mean it is certified yet — certification flows through
itemlochistoryat a certificate.
Certificates and payment
When importing contract administration history:
- Snapshot source =
itemlochistory(per item, location, certificate):pvalqty × itemprice. - Roll-up ties to
ContractorCertificates.WorkExecuted→ccrcert.certvalue(allow small rounding viaCertificateDecimals). - Amount payable this certificate = marginal difference vs previous (
PreviousCertificatewhen present). - Retention is a named
Recordsline (e.g. RecordTypeEnumId=4 "10%" / "7.5%"), not a hidden constant for the whole contract. - Audit trail = merge
JbccHistorywithprojecthistory(lock/unlock entries name certificate numbers).
Escalation caveat
Haylett escalation may run and produce populated rows while still paying R0 on every row. Always check amounts — do not treat presence of escalation records as proof of a claim.
Validation checklist
Before treating an import as production-ready:
- Row counts for chosen doctypes match source (structure sections, items, dimensions).
- For a sample measured item:
itemloc.measqtyequalsdimension.dimtotal. - DimX batches: provenance present; itemprice empty expected; createuser sentinel understood.
- No importer logic reads item.vono for variations.
- BoE and BoQ section trees imported under separate document roots.
- If certificates imported: exclude ccrcertno=0; spot-check one certificate's marginal payment.
- Print/heading order preserved for a trade section (H1→H4→TX→measured).
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No DimX rows found in SpoormakerDimXItems | Table unused on this project | Query item where createuser='dimX' + extimport* |
| BoE full of Model Preambles text | Query not filtered by doctype | Restrict to doctype='E' vs 'Q' |
| Variation report returns DimX items | Filtered on item.vono | Use dimension.vono + variation table |
| Certificate 'amount due' looks huge | Reported cumulative certvalue | Subtract previous certificate / PreviousCertificate |
| GP constants missing | Looked in gpconstant | Load doctype='G' structure items |
| Contractor names blank | Joined Contractors (legacy) | Use contractor.PersonId → Persons |
Next steps
- Open the product page: Switch from WinQS
- Start a QSNAPP project and prepare your first slice: Sign up
- For agent / lab workflows, query qs-brain locally:
cd qsnapp-web/knowledge/qs-brain graphify query "how does DimX import work?" graphify explain "measurement chain"