# Opportunities: the deal record

An opportunity is one commercial deal, attached to the person and company it belongs to, carrying a value in minor units with its currency, a stage, and a flag saying whether it needs a person's attention. It is opened, valued, updated, moved and closed through `sales.py`, and every one of those verbs leaves an audit entry.

- **Status:** Available
- **Audience:** both
- **In the app:** #/pipeline, #/data, #/relationships
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/sales/opportunities/

## What the record holds

| Field | Set by | What reads it |
|---|---|---|
| What the deal is for | `open_opportunity`, then `update` | The board, the Assistant, and any summary of the relationship |
| Value, in minor units | `set_value`, as `money(value_minor, currency)` | Every total on the board; arithmetic never touches a decimal string |
| Currency | Set with the value, not separately | Formatting, and the refusal to add two currencies together |
| Stage | `move`, subject to stage rules | The column the card sits in, and what the next action is |
| The person and company | The relationship it was opened from | Context for a reply; the merge path in relationships |
| Review flag | `raise_for_review` / `clear_review` | Needs You — this is how a deal asks for a human |
| Demo state | `request_demo`, `schedule_demo`, `complete_demo`, `cancel_demo` | The follow-up that a scheduled demo implies |

Minor units are the field worth pausing on. A value is stored as a whole number of the currency's smallest unit — 1250000 rather than 12500.00 — so no total depends on a decimal point surviving a JSON round trip, a spreadsheet import or a locale that writes its separators the other way round. Formatting back to something a person reads happens at the edge, once.

## The verbs, and what each one guarantees

**`open_opportunity`** — Creates the record against a relationship. A deal with no person or company attached is possible and is nearly always a mistake worth correcting immediately.
**`set_value`** — Writes value and currency together. Changing the value later is an update to the record, not a new deal, so the board's history stays honest.
**`update`** — Changes descriptive detail without touching stage or value. This is the safe verb, and the one the Assistant reaches for most.
**`move`** — Attempts a stage change and consults the stage rules first. It can decline, and a decline is an ordinary answer.
**`raise_for_review` / `clear_review`** — Puts the deal in front of a person and takes it back out again. The pair is what makes escalation reversible without deleting anything.

> **Note** The Assistant can open, move and update an opportunity through the same service a person's own click uses. Its rights are deliberately narrower elsewhere — pricing is not one of the things it may decide.

## What the record does not carry

Two absences are deliberate and are worth knowing before you go looking for a field that is not there.

- **Agreed commercial terms.** A price Connect has quoted, an SLA it has committed to, a warranty it has promised — none of these exist as an opportunity field, because Connect does not create them. Terms come from Knowledge or from a person, and the deal records the outcome rather than authoring it.
- **A forecast Connect invented.** Nothing on the record is a model's guess about whether the deal will close. What the record holds is what somebody did and when.

Both absences follow from the same principle: the record is evidence, and evidence that a machine wrote about its own optimism is not evidence.

## Reading and editing deals in bulk

The deals sheet in `#/data` is the same records in a grid — sortable, filterable, groupable, with saved views, inline editing and CSV export. It is not a second database: every change goes through the sales service that owns the record, so a bulk edit is audited exactly like a click on the board. CSV export neutralises formulas on the way out.

> **Careful** A value pasted into the grid as `12,500.00` is a string a human recognises and a machine has to interpret. Enter values the way the grid asks for them, and check one row after a bulk import before trusting the column total.

## Questions

### Why are values stored as integers?

Because currency arithmetic in decimals is where money quietly goes missing. Minor units make every total exact and every comparison unambiguous, and the conversion to a readable figure happens once, at the point of display.

### Can one deal belong to two companies?

It is attached to the relationship it was opened from. Where two organisations are genuinely involved, keep the deal on the one that will sign and record the other on the timeline — splitting a single commitment across two records makes both of them wrong.

### What happens to the deal if its person is merged into another record?

It survives the merge. Deals, along with identities, stages, follow-ups, demos, cases and onboarding, are preserved from both sides — see [how a deal connects to people and companies](/docs/sales/deal-relationships/).

## Related

- [Sales in Connect](https://connectbyjbrh.com/docs/sales/)
- [The pipeline board](https://connectbyjbrh.com/docs/sales/pipeline/)
- [Stage rules and refusals](https://connectbyjbrh.com/docs/sales/stage-rules/)
- [How a deal connects to people and companies](https://connectbyjbrh.com/docs/sales/deal-relationships/)
- [Creating an opportunity, end to end](https://connectbyjbrh.com/docs/workflows/create-opportunity/)
- [Files and data in Connect](https://connectbyjbrh.com/docs/files-data/)

## What this page is based on

- docs-source/sources/CHANNELS.md §6 — sales verbs, money and safe_sales
- docs-source/sources/GENERAL.md §8 and §9 — Assistant rights and the data grid
- Connect capability registry (docs-source/facts.py)
