Files
twenty/packages/twenty-apps
martmull e1b5edc07e Compute last contact on relationship changes in last-contact app (#23569)
## What

The `last-contact` app only refreshed the last contact on Companies and
Opportunities when a new email or meeting arrived. When relationships
changed but no interaction happened, those fields went stale:

- Creating an opportunity with an existing point of contact left its
last contact empty.
- Changing an opportunity's point of contact kept the previous contact's
value.
- Assigning a person (who already had contact history) to a company
never surfaced on the company.

This adds logic functions that recompute the derived last-contact fields
when the record or its relationships change.

## Changes

New logic functions (auto-discovered):

- `on-opportunity-created` (`opportunity.created`) and
`on-opportunity-updated` (`opportunity.updated`, `pointOfContactId`)
recompute an opportunity's last contact from its point of contact.
- `on-company-created` (`company.created`) recomputes a company's last
contact from its people.
- `on-person-created` (`person.created`) and `on-person-updated`
(`person.updated`, `companyId`) recompute the former and current
company's last contact when a person joins or leaves.

Shared helpers `recomputeOpportunityLastContact` and
`recomputeCompanyLastContact` mirror the point-of-contact /
most-recent-person value onto the record (clearing it when there is no
contact). Reads use the morph relation subfield (`lastContactItemMessage
{ id }`), matching the existing integration-test read pattern.

The `updatedFields` filters keep these off the interaction write path,
so they never self-trigger.

## Tests

- Unit tests for both recompute helpers and the new logic functions.
- Integration tests covering opportunity-on-create, point-of-contact
change, person joining/leaving a company, and the empty-company case.
- `typecheck`, `lint`, and unit tests pass.

---
_Generated by [Claude
Code](https://claude.ai/code/session_014LMuzNLGYrL5eTkMB3UDRV)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23569?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-30 14:24:05 +00:00
..