# Customer and supplier workload

The second Phase 5 reporting milestone adds **Relationships** on staff web, iOS and Android. It connects each directory company with the current records needing attention. Staff can search company name or country, filter customer/supplier/both and active/archived/all, inspect a company, switch among six record categories and paginate the underlying records. Returning to the company list retains its filters and page. Each record shows its title/reference, ID, state, version, update timestamp and relevant deadline. A source-workspace button opens the existing workspace; it does not jump directly to an individual editor.

This is a current workload report, not historical sales analytics, a supplier reliability score, customer ranking or an AI recommendation. It computes no monetary sums or margins and makes no commercial decisions. Counts overlap and must not be summed into a number of distinct opportunities.

## Selection rules

The API uses one shared source selector for each count and its drill-down so the definitions cannot diverge:

- **Open conversations:** open inbox records explicitly linked to the company. Unlinked messages are excluded; message text and contact addresses are not returned by the report.
- **Active offer records:** company-linked supplier offers in `active` state, with no expiry or an expiry strictly after the snapshot. Record counts are not available devices. An archived company can retain active record states; the report deliberately shows these for review without asserting trading eligibility.
- **Active wanted records:** the same state/expiry rule for wanted records. This is a number of records, not unfilled quantity or remaining demand.
- **Pending quotations:** quotations in `pending` state with this current customer. Expired/changed sources remain included until staff resolve the submitted quotation; the count is not approval eligibility.
- **Overdue follow-ups:** open tasks due at or before the snapshot, joined to the quotation's current customer. Changed, expired or cancelled quotations do not hide unresolved tasks. This uses the current quotation relationship, not a claim about historical customer ownership at task creation.
- **Open orders:** pending, reserved, allocated, dispatched or returning orders, attributed through their quotation's customer. These orders retain the existing active-quotation lock that blocks changing the quotation's customer. Delivered, returned and cancelled orders are excluded. In particular, cancelled order history is not reassigned when its quotation is revised for another customer. Expired holds remain visible until resolved but do not imply stock remains reserved. An order's original hold deadline is historical once allocated/dispatched.

Archived companies are searchable and retain linked unresolved records. Company edits do not rewrite frozen quote/order snapshots; this report shows the current directory name and relationship. No company link is inferred from a matching email address, phone, name, or text. Supplier-source attribution inside quotation lines, purchases, fulfilment reliability and historical transactions need a separate reporting design.

## API and consistency

- `GET /v1/relationships?q=&kind=all&status=active&limit=20&offset=0`
- `GET /v1/relationships/{company_id}?category=conversations&limit=20&offset=0`

Both return an `as_of` UTC timestamp. Each request executes a single SQL reporting statement, so summary counts, source rows and pagination total come from the same statement snapshot. Directory results sort by lowercase company name and ID; sources sort by latest recorded update then ID. Counts remain available on empty/out-of-range pages. Maximum page size is 50. Search escapes SQL wildcard characters; a search for `%`, `_` or a backslash is literal. Invalid IDs/filters/page bounds are rejected and absent companies return 404.

Separate requests are fresh reads: records can change between pages or between directory and drill-down requests. Refresh reports current state; it does not hold locks or reserve data. Hooks key results to the complete query and refresh revision, clear obsolete data immediately, and ignore replies after a category/filter changes or the screen unmounts. Failures display an error and retry control, not false zeros. No background polling is enabled.

All active authenticated staff roles can read the report, matching the shared staff directory/inbox/trading permissions. Personal data is limited to existing company name/country plus record titles/references; message bodies, email addresses, IMEIs, prices and internal notes are excluded. Session revocation/inactive accounts remain enforced. Responses are `no-store`. The endpoint adds no mutation method, audit event, business record or database migration.

## Validation and boundaries

API tests cover all roles, empty/missing companies, all six definitions and drill-down counts, equality at expiry/due boundaries, cross-company/unlinked isolation, archived companies, literal search, paging, invalid input, no writes, one reporting statement and revoked sessions. Browser tests cover real company/message links, company and source pagination, filter preservation, archived viewer access at 390px, markup escaping, source-workspace navigation, stale replies, outages and session expiry. Native device interaction and PostgreSQL runtime/performance checks remain pending; mobile exports alone are not physical-device validation.

Formal invoice issuance remains disabled pending issuer/tax details. Live email testing remains deferred, WhatsApp is uninitialized, and this feature activates no integration, sends no message and changes no live website or account credentials.

For dated quotation/order activity, open **View trading history** from a company’s workload. This uses immutable customer attribution rather than current links; see COMPANY-HISTORY.md.
