# Schema Reference — Construction

Vertical schema for `businessType: "construction"` — small building firms and independent building contractors (the SiteOffice brand).

This reference extends the base schema. All base node types (Person, Organization, Invoice, PostalAddress, Event, etc.) remain valid. Load `schema-base.md` first.

When loading this reference, confirm: "Using schema-base + schema-construction".

---

## Additional Node Types

| Entity | Neo4j Label | Schema.org Type | Required Properties |
|--------|-------------|-----------------|---------------------|
| Job | `Job` | `schema:Project` | `accountId`, `jobId`, `client`, `address`, `status` (quoting/active/complete), `contractValue`, `awardedDate` |
| LineItem | `LineItem` | `cdm:LineItem` | `accountId`, `jobId`, `trade`, `description`, `contractValue` |
| Valuation | `Valuation` | `schema:MonetaryAmount` | `accountId`, `jobId`, `valuationNumber`, `date`, `totalValue` |
| Milestone | `Milestone` | `schema:Event` (subtype) | `accountId`, `jobId`, `name`, `completedDate` |
| QuoteDocument | `QuoteDocument` | `schema:DigitalDocument` | `accountId`, `jobId`, `ref`, `amount`, `date`, `status` (draft/sent/accepted) |
| VariationNote | `VariationNote` | `schema:DigitalDocument` | `accountId`, `jobId`, `variationNumber`, `description`, `amount`, `status` |
| InboundInvoice | `InboundInvoice` | `schema:Invoice` | `accountId`, `supplier`, `invoiceNumber`, `amount`, `status` (pending/approved/processed) |
| SubContractor | `SubContractor` | `schema:Person` (extended) | `accountId`, `name`, `whatsappNumber`, `dayRate` |
| TimeLog | `TimeLog` | `schema:Action` | `accountId`, `subContractorId`, `jobId`, `date`, `days` |
| SubInvoice | `SubInvoice` | `schema:Invoice` | `accountId`, `subContractorId`, `jobId`, `period`, `days`, `amount`, `status` |
| WhatsAppGroup | `WhatsAppGroup` | `cdm:Channel` | `accountId`, `groupId`, `jobId`, `clientName` |

These entities are grounded in real builder job folders (the G.L. Smith & Sons "1845 — 12 Hickman Close" job: the `Q-1845` quote, the `1845.xlsx` Val/BDS 40+ line-item sheet, supplier invoices, and the per-trade structure). The 40+ `LineItem` rows per `Job` mirror the Val/BDS trade structure: Groundworks, Sub-Structure, Superstructure, Roof, Structural Alterations & Envelope, First Fix, Drylining & Plastering, Floor Screed, 2nd Fixing, Knock Throughs, Windows & Doors, Completion Items, Preliminaries, plus sub-contractor trades (Scaffolding, Roofing, Electrical, Plumbing, Plastering, Tiling, Decorations).

### Distinctions from schema-trades

`QuoteDocument` is the generated client quote artefact (the `Q-XXXX` document), distinct from schema-trades' `Quote` (a pre-acceptance Order estimate). `InboundInvoice` and `SubInvoice` are received/payable invoices with job + trade assignment, distinct from the base `Invoice`. `SubContractor` extends base `Person` with `dayRate` and `whatsappNumber`; use it as an additional label on a `Person` node where the subcontractor is also a known person, or standalone where only the WhatsApp identity is known.

---

## Idempotency contracts

MERGE keys per label:

- `:Job` — `(accountId, jobId)`
- `:Valuation` — `(accountId, jobId, valuationNumber)`
- `:QuoteDocument` — `(accountId, ref)`
- `:VariationNote` — `(accountId, jobId, variationNumber)`
- `:InboundInvoice` — `(accountId, supplier, invoiceNumber)`
- `:SubContractor` — `(accountId, whatsappNumber)`
- `:SubInvoice` — `(accountId, subContractorId, period)`
- `:WhatsAppGroup` — `(accountId, groupId)`
- `:LineItem` — `(accountId, jobId, trade, description)` — child of `:Job`, write-time MERGE only
- `:Milestone` — `(accountId, jobId, name)` — child of `:Job`, write-time MERGE only
- `:TimeLog` — `(accountId, subContractorId, jobId, date)` — write-time MERGE only

---

## Relationship Patterns

```
(:Job)-[:HAS_LINE_ITEM]->(:LineItem)
(:Job)-[:HAS_VALUATION]->(:Valuation)
(:Job)-[:HAS_MILESTONE]->(:Milestone)
(:Job)-[:HAS_QUOTE]->(:QuoteDocument)
(:Job)-[:HAS_VARIATION]->(:VariationNote)
(:Job)-[:FOR_CLIENT]->(:Person)
(:Job)-[:AT_ADDRESS]->(:PostalAddress)
(:InboundInvoice)-[:ASSIGNED_TO]->(:Job)
(:InboundInvoice)-[:FROM_SUPPLIER]->(:Organization)
(:SubContractor)-[:LOGGED]->(:TimeLog)
(:TimeLog)-[:ON_JOB]->(:Job)
(:SubInvoice)-[:FOR_SUBCONTRACTOR]->(:SubContractor)
(:SubInvoice)-[:ON_JOB]->(:Job)
(:WhatsAppGroup)-[:MAPPED_TO]->(:Job)
```

Every `:Job` is the parent node for its line items, valuations, milestones, quotes, and variations, satisfying the ≥1-adjacency write-gate doctrine. A `:Job` links to its client (`:Person`) and site (`:PostalAddress`); inbound supplier invoices link to the `:Job` they are assigned to and the `:Organization` that issued them.
