1. Purpose
An Estimate is a priced build-up inside a Tender — the container for a submission. A Tender can have multiple Estimates (base, alternative, strategy variants) to submit different versions or strategies; only one is typically submitted at a time.
Estimate is tightly coupled to Tender: Tender state cascades to Estimates (BR-094). See tender.md §4 for cascade rules.
2. Attributes
| Attribute | Type | Required | Default | Notes |
|---|---|---|---|---|
id | UUID | ✅ | generated | System-managed |
tender_id | Tender ref | ✅ | — | Parent Tender. Exactly one per Estimate |
name | string (text) | ✅ | — | Estimate name; e.g., “Base Case” or “Alternative 2” |
estimate_number | string (free text) | ✅ | — | Internal variant identifier (e.g., “rev a”, “alt”, “1”). Appears in publisher output |
lead_estimator_id | User ref | ✅ | — | Assigned Lead Estimator. Required (BR-093). Nominal accountability only — NOT an access-control gate (BR-095, roles-permissions.md §2) |
status | enum | ✅ | In Progress | In Progress · Reviewed · Submitted · Archived. See §4 |
notes | long text | ❌ | null | Estimate-level notes |
created_at / updated_at | timestamp | ✅ | system | Audit |
created_by / updated_by | User ref | ✅ | system | Audit |
Note on Lead Estimator (BR-093): The Lead Estimator field identifies who is primarily accountable for reporting and audit trail purposes. It does not restrict who can edit, lock, submit, or publish — those capabilities are driven by Role alone. Any User with Lead Estimator Role can perform Lead Estimator actions on any Estimate.
3. Relationships
| Direction | Entity | Cardinality | Required | Notes |
|---|---|---|---|---|
| Outbound | Tender | Estimate M:1 Tender | ✅ | Parent; cascade from Tender state (BR-094) |
| Outbound | Lead Estimator (User) | Estimate M:1 User | ✅ | Accountability; not access-control gate (BR-093) |
| Inbound | Heading | Estimate 1:M Heading | — | Top-level Headings; root of the work tree |
| Inbound | Rule | Estimate 1:M Rule | ❌ | Commercials rules scoped to this Estimate |
| Inbound | Publisher Output | Estimate 1:0..1 Publisher Output | — | Latest submission artifact only (BR-075); absent until first publish |
| Inbound | Price Book Adjudication | Estimate 1:M Price Book Adjudication | — | Adjudications scope over this Estimate’s Resources |
| Inbound | Subcontract Package | Estimate 1:M Subcontract Package | ❌ | Optional subcontract packages within this Estimate |
4. Lifecycle States
| State | Meaning | Trigger | Manual/Auto | Notes |
|---|---|---|---|---|
| In Progress | Initial state. Estimate is being built and edited. Items are priced; Worksheets active. | Estimate creation | Auto | Default on creation |
| Reviewed | Lead Estimator has reviewed. Anomaly checks passed or accepted. Ready for submission. | Automatic when ALL Items reach status = Reviewed | Auto (derived) | Signals readiness; no manual transition needed. Estimate Reviewed state is derived from child Item state. |
| Submitted | Estimate submitted to client (via Publisher). Fully locked — no fields editable (BR-094). Submit is gated — blocked if any Item status is Unpriced or Plugged (BR-019c). Estimators can navigate to Publisher preview; only final Submit action checks the gate and fires an anomaly list if items are not submission-ready. Required Item states: Priced, Reviewed, or Locked (Reviewed is optional; Priced is sufficient). | Publisher publishes | Auto (on publish) | Once locked, only Admin can unlock (soft delete / recovery scenario) |
| Archived | Estimate no longer active. Read-only; recoverable. | Tender state cascade or manual transition | Auto/Manual | Triggered by Tender loss/archive (auto) or manual archival (manual) |
Key note: In Progress → Reviewed transition is automatic and derived — it triggers when every Item in the Estimate reaches Reviewed status. Reviewed → In Progress can occur manually (Lead Estimator reopens) or automatically (e.g., if any Item drops below Reviewed via rate-change cascade per BR-049a).
State transition diagram:
┌──────────────────┐
│ In Progress │
│ (editing allowed)│
└────┬───────────┬─┘
│ │
(review) (archive)
│ │
▼ │
┌──────────────┐ │
│ Reviewed │ │
│ (ready) │ │
└────┬─────────┘ │
│ │
(publish) │
│ │
▼ ▼
┌──────────────────────────────┐
│ Submitted (Locked) │
│ (no edits; awaiting cascade) │
└──────────────────────────────┘
Locked behaviour (BR-094):
- Once Submitted, an Estimate is read-only — all Item edits, Worksheet changes, commercial rule edits are blocked
- Cascade from Tender state (Won/Lost/Archived) moves Estimate to Archived (read-only, uneditable)
- Only Admin can unlock (recovery scenario; rare)
Submit transition mechanics (BR-019c):
- Submit is user-initiated but conditional: the transition checks that all child Items are submission-ready (Priced, Reviewed, or Locked).
- Estimators can navigate to Publisher preview and review the output regardless of Item status.
- The final Submit action itself is gated — if any Item is Unpriced or Plugged, the action fires an anomaly list rather than committing the Submitted state.
5. Real-time Collaboration (BR-095)
Model: Multiple Users with access can open the same Estimate simultaneously. Editing locks apply at Item granularity:
- When a User begins editing an Item (or any Worksheet children), the Item enters a lock state
- Other Users see it as “currently edited by [User]” — read-only on that Item
- Lock releases on:
- User closes / navigates away from the Item
- Idle timeout (default 10 minutes — admin-configurable)
- Admin override
Concurrent editing on different Items is unrestricted.
🟡 Flagged for Oxcon: confirm explicit per-Item locking model vs. Google-Docs-style field-level concurrent editing (more complex but more elegant).
6. Validation / Invariants
- One Tender per Estimate. Every Estimate’s
tender_idpoints to exactly one Tender. - Lead Estimator required. Every Estimate has a
lead_estimator_idpointing to a User with Lead Estimator role. BR-093. - State enum. Estimate status ∈ {In Progress, Reviewed, Submitted, Archived}.
- Archived non-reversible via state machine. Archived Estimates cannot be “un-archived” via state machine (Admin recovery only).
- Unique identification.
estimate_numberis free-text and may collide globally, but within a Tender it should be unique (soft constraint — UI prevents collision). - Submit guard. Cannot transition Estimate to Submitted when any child Item has status ∈ {Unpriced, Plugged}. All Items must be Priced, Reviewed, or Locked. (BR-019c).
7. Worked Examples
For end-to-end Tender + Estimate flows (base/alternative/strategy variants, won/lost cascades), see tender.md §6 Worked Examples. Both example scenarios there illustrate how Estimate states interact with Tender states.