1. Purpose
A Company is an external organisation — client, supplier, subcontractor, or any combination thereof. Companies are synced from Xero (the financial source of truth) in a one-way, read-only pattern; they cannot be created directly in oxFlow. Xero is and will remain the source of truth; oxFlow mirrors Company data from Xero only. Each Company carries one or more Company Roles that determine where and how it can be assigned in the workflow.
Analogy (glossary cooking model): the Company is the vendor or customer — an external entity that participates in the estimate and cost lifecycle.
2. Attributes
| Attribute | Type | Required | Default | Notes |
|---|---|---|---|---|
id | UUID | ✅ | generated | System-managed |
name | string (text) | ✅ | — | Legal name of the organisation; e.g., “Fletcher Construction Ltd” |
xero_id | string (unique) | ✅ | — | Xero’s unique identifier for this organisation. Immutable; prevents duplicate syncs |
address | long text | ❌ | null | Physical address (street, suburb, postcode). Synced from Xero |
contact_info | long text | ❌ | null | Phone, email, or other contact details. Free-form; synced from Xero |
notes | long text | ❌ | null | oxFlow-only free-text notes; e.g., “added Subcontractor role 15 Apr after verbal agreement” |
company_roles | multi-select | ✅ | [Supplier] | One or more of: Client · Supplier · Subcontractor (BR-082). Initialised from Xero metadata on sync; editable in oxFlow post-sync (BR-083) |
created_at / updated_at | timestamp | ✅ | system | Audit |
created_by / updated_by | User ref | ✅ | system | Audit. created_by is always system (Xero sync); updated_by tracks oxFlow edits |
3. Company Roles
Company Role is a multi-select attribute — a single Company can carry any combination of the three roles. Each role governs assignment scope in oxFlow.
| Role | Purpose | Assignment scope | Notes |
|---|---|---|---|
| Client | Purchaser of the work; issues the Tender | Tender (as client_id) | A Tender must have exactly one Client Company (BR-090) |
| Supplier | Provides resources or services via Price Book | External Price Book (as supplier_id) | Set at Price Book creation. Can compete in Price Book Adjudication |
| Subcontractor | Bids for Subcontract Packages | Subcontract Package (via Adjudication) | Can compete in Subcontract Package Adjudication |
Defaulting behaviour (BR-083):
- Xero customer → Client role (on sync)
- Xero supplier → Supplier role (on sync)
- Ambiguous or no metadata → Supplier role (safe default)
- Admin can assign any role combination post-sync; roles are editable without re-syncing
4. Lifecycle States & Sync
Companies have no lifecycle state machine. Once synced from Xero, a Company persists in oxFlow with:
- name, xero_id, address, contact_info — read-only from Xero
- company_roles, notes — editable in oxFlow (not synced back to Xero)
Sync mechanics:
- One-way (locked): Xero → oxFlow. Triggered periodically or on-demand. Updates to Company attributes in Xero are reflected in oxFlow on the next sync. Duplicate detection via
xero_id. No bi-directional sync is planned.
Archival:
- When a Company is archived in Xero, it is automatically archived in oxFlow on the next sync. Archived Companies remain visible in oxFlow in read-only mode (e.g., for historical reporting) but cannot be selected for new Tenders, Price Books, or Adjudications (BR-080a).
5. Relationships
Inbound (things referring to Company)
| From | Cardinality | Role context | Notes |
|---|---|---|---|
| Tender | Tender M:1 Company | Client | BR-090: every Tender must have a Client Company; enforced at Tender creation |
| Price Book | PriceBook M:0..1 Company | Supplier | External Price Books only; required if source_type = External |
| Price Book Adjudication | PBA M:M Company | Suppliers | Multiple Companies compete with pricing proposals |
| Subcontract Package Adjudication | SPA M:M Company | Subcontractors | Multiple Companies bid for subcontracted scope |
Outbound (things Company references)
| To | Cardinality | Required | Notes |
|---|---|---|---|
| Company Role | Company M:M CompanyRole | ✅ | Multi-select; minimum one role required (BR-082) |
6. Validation / Invariants
Rules that must hold at all times:
- Xero ID unique. No two Companies may share the same
xero_id. Enforced at sync time. - Name required.
namecannot be null. - At least one role.
company_rolesmust contain at least one of: Client, Supplier, Subcontractor. Never empty (BR-082). - Role enum.
company_rolesvalues must be one of the three defined Roles. No other values accepted. - No circular references. (N/A for Company; no self-references in relationships.)
- Immutable Xero ID. Once synced,
xero_idcannot be changed or transferred to another Company (orphaning prevents this).
7. Derived / Computed Attributes
| Attribute | Derivation | Notes |
|---|---|---|
is_client | company_roles.contains("Client") | Boolean flag for UI filtering |
is_supplier | company_roles.contains("Supplier") | Boolean flag for UI filtering |
is_subcontractor | company_roles.contains("Subcontractor") | Boolean flag for UI filtering |
tender_count | Count of Tenders where client_id = this Company | For informational dashboards |
price_book_count | Count of External Price Books where supplier_id = this Company | Usage tracking |
8. Worked Examples
Example A — Fletcher Construction Ltd (Client only)
A Xero customer synced as a Client. Used on five Tenders as the purchaser:
Company:
name: "Fletcher Construction Ltd"
xero_id: "XERO-F1428-C" (unique Xero reference)
address: "Level 5, 150 Queen Street, Auckland, NZ 1010"
contact_info: "Tel: 09-888-5555 | Contacts: James Riley (james.r@fletcher.co.nz)"
company_roles: ["Client"] (synced from Xero customer; not Supplier)
notes: null
created_at: 2026-03-01T09:30:00Z (sync timestamp)
created_by: system (Xero sync)
Relationships:
Tender 1: "Auckland Waterfront Carpark" (Fletcher client)
Tender 2: "Industrial Estate Extension" (Fletcher client)
Tender 3: "Retail Complex Fit-out" (Fletcher client)
Tender 4: "Commercial Tower Lobbies" (Fletcher client)
Tender 5: "Basement Level Works" (Fletcher client)
Derived:
is_client = true
is_supplier = false
is_subcontractor = false
tender_count = 5
Example B — Pacific Steel (Supplier → Supplier + Subcontractor)
A Xero supplier initially synced as Supplier. Admin later adds Subcontractor role when invited to bid on subs:
Company:
name: "Pacific Steel Ltd"
xero_id: "XERO-PS-2847"
address: "1 Tauranga Road, Port of Tauranga, NZ 3112"
contact_info: "Tel: 07-571-9500 | procurement@pacificsteel.co.nz"
company_roles: ["Supplier", "Subcontractor"] (Supplier synced; Subcontractor added 17 Apr by Admin)
notes: "Added Subcontractor role when invited to bid on GF slab formwork package. Preferred supplier for structural steel; good capacity for subcontract work."
created_at: 2026-02-15T14:22:00Z (Xero sync)
updated_at: 2026-04-17T10:18:00Z (role added in oxFlow)
created_by: system
updated_by: admin@oxflow.local
Relationships:
Price Book: "External — Pacific Steel Q2 2026" (as Supplier)
Subcontract Package Adjudication #42: "Ground floor slab formwork" (as bidding Subcontractor)
→ winning bid awarded 23 Apr; $45.75/m² for 36m²
Derived:
is_client = false
is_supplier = true
is_subcontractor = true
price_book_count = 1
(tender_count = 0, not a client)