Phones, addresses and social profiles: one behaviour for companies, contacts and Your details #934

Closed
opened 2026-10-16 13:22:16 +00:00 by tiagoagueda · 0 comments
Owner

Telephone numbers, postal addresses and social profiles are stored in the same tables for a company, a contact and the user's own details. But they are edited, shown, exported and offered through the API slightly differently for each. They should behave the same everywhere, and any difference should be on purpose and written down.

What exists today

  • Storage is already shared. All three use one table with a generic holder (content_type + object_id): PhoneNumber, PostalAddress and WebLink in core/models.py. The fields and choices are the same for every owner, and so are the partials that edit them (partials/phone_numbers.html, postal_addresses.html, web_links.html).
  • Phone kinds are Mobile, Work, Home, Switchboard, Fax and Other.
  • Address kinds are Home, Work, Postal and Other.
  • Social services (LinkedIn, Mastodon, Bluesky, X, Xing, Facebook, Instagram, Threads, YouTube) are the same list for every owner.

Where they differ (verified on main)

How rows are edited

  • The profile has different controls. It passes remove_at_once and card_help, which give it the star for primary (<c-primary-choice>), a bin that removes a row at once, and a help mark. A company and a contact get a plain "Primary" radio and a Remove checkbox (phone_numbers.html:69-77). The profile also builds its numbers and addresses itself (accounts/views.py:71-105) rather than through the mixins in core/mixins.py:158-314 that companies and contacts use.

Phones

  • Advice on extensions. A number written with an extension gets a different refusal: companies and contacts are told to put the extension in the notes (has_notes, core/phone_numbers.py:666), while the profile is only told to leave it out. None of the three owners has an extension field.
  • vCard. A contact exports every number with its kind. A company exports none and drops TEL when a card is imported (jobs/vcards.py:111-122, 266-270). The profile exports only its printed primary number.
  • API. A company takes and returns a phone_numbers list. A contact takes a single phone + phone_country (api/routers/companies.py:303-322).

Addresses

  • Map. A company's addresses have no "Show on the map" choice: they are always drawn (addresses_pinnable=False, jobs/views.py:1134, 1162). Contacts and the profile get the choice when Maps is on.
  • Browser autofill. Only the profile's addresses offer it. Company and contact rows are autocomplete="off" (core/postal.py:388).
  • Display. A company's addresses are listed on its page. A contact's addresses are not shown on the contact cards (company_detail.html:478-511).
  • vCard. A contact exports all its addresses, a company exports none, and the profile exports its location address.
  • API. A company has postal_addresses. ContactIn / ContactOut have no addresses at all (api/schemas.py:308-322, 413).

Social profiles

  • The LinkedIn prefix. It is the person's /in/ for every owner, so a company row suggests a personal address (#932).
  • vCard. A company exports only its website, not its social profiles. A contact exports every link, and the profile its printed primary ones.
  • API. A company's web_links takes only the social kind. A contact also still takes the old single linkedin_url (api/routers/companies.py:282-287).
  • Link kinds. A company is offered social profiles only, its website being the Company.website column. Contacts and the profile also get code repositories and websites. This difference may be deliberate, but it is not written down.

Differences that are on purpose (keep, and document)

  • Phone uniqueness. Only the profile's numbers are unique across the instance and count against the collision rate limit, because a person's number identifies them (identifies_owner, phone_numbers.py:138, 284).
  • Account recovery. Gets me back in is offered only on the profile, and only when a text gateway is installed.

What would change

  1. The same controls everywhere. The star for primary, the bin that removes at once, the help mark (#918) and the section layout (#919, #931) work the same way for a company, a contact and the profile. One code path builds the formsets, so the profile no longer has its own get_numbers / get_addresses.
  2. The same rules everywhere. Use the same extension advice, the same autofill rule, and the same map choice (#923). For a company, either give it the choice like the others, or write down why it is always drawn. Apply #932 so each owner gets the right service prefix.
  3. The same display. A contact's card shows its addresses as a company's page does, subject to the person's privacy choice.
  4. The same vCard. Companies export and import their numbers, addresses and social profiles. The profile's "printed primary only" is a choice about what a CV shows, so it should be kept apart from what the vCard holds, and it should be decided and written down.
  5. The same API. Contacts get phone_numbers, postal_addresses and web_links lists like companies. The single phone / phone_country and linkedin_url fields are deprecated and kept as aliases for a release.
  6. Write the deliberate differences down. Uniqueness, recovery, and which link kinds a company gets go in one place: the docstrings of the shared models and the wiki's page on contact details. A test pins each one, so a difference can't come back unnoticed.

#918, #919, #922, #923, #925, #931, #932.

Telephone numbers, postal addresses and social profiles are stored in the same tables for a company, a contact and the user's own details. But they are edited, shown, exported and offered through the API slightly differently for each. They should behave the same everywhere, and any difference should be on purpose and written down. ## What exists today - **Storage is already shared.** All three use one table with a generic holder (`content_type` + `object_id`): `PhoneNumber`, `PostalAddress` and `WebLink` in `core/models.py`. The fields and choices are the same for every owner, and so are the partials that edit them (`partials/phone_numbers.html`, `postal_addresses.html`, `web_links.html`). - **Phone kinds** are Mobile, Work, Home, Switchboard, Fax and Other. - **Address kinds** are Home, Work, Postal and Other. - **Social services** (LinkedIn, Mastodon, Bluesky, X, Xing, Facebook, Instagram, Threads, YouTube) are the same list for every owner. ### Where they differ (verified on `main`) **How rows are edited** - **The profile has different controls.** It passes `remove_at_once` and `card_help`, which give it the star for *primary* (`<c-primary-choice>`), a bin that removes a row at once, and a help mark. A company and a contact get a plain "Primary" radio and a *Remove* checkbox (`phone_numbers.html:69-77`). The profile also builds its numbers and addresses itself (`accounts/views.py:71-105`) rather than through the mixins in `core/mixins.py:158-314` that companies and contacts use. **Phones** - **Advice on extensions.** A number written with an extension gets a different refusal: companies and contacts are told to put the extension in the notes (`has_notes`, `core/phone_numbers.py:666`), while the profile is only told to leave it out. None of the three owners has an extension field. - **vCard.** A contact exports every number with its kind. A **company exports none** and drops `TEL` when a card is imported (`jobs/vcards.py:111-122, 266-270`). The profile exports only its printed primary number. - **API.** A company takes and returns a `phone_numbers` list. A **contact takes a single `phone` + `phone_country`** (`api/routers/companies.py:303-322`). **Addresses** - **Map.** A company's addresses have **no "Show on the map"** choice: they are always drawn (`addresses_pinnable=False`, `jobs/views.py:1134, 1162`). Contacts and the profile get the choice when *Maps* is on. - **Browser autofill.** Only the profile's addresses offer it. Company and contact rows are `autocomplete="off"` (`core/postal.py:388`). - **Display.** A company's addresses are listed on its page. A **contact's addresses are not shown** on the contact cards (`company_detail.html:478-511`). - **vCard.** A contact exports all its addresses, a **company exports none**, and the profile exports its location address. - **API.** A company has `postal_addresses`. **`ContactIn` / `ContactOut` have no addresses at all** (`api/schemas.py:308-322, 413`). **Social profiles** - **The LinkedIn prefix.** It is the person's `/in/` for every owner, so a company row suggests a personal address (#932). - **vCard.** A **company exports only its website**, not its social profiles. A contact exports every link, and the profile its printed primary ones. - **API.** A company's `web_links` takes only the social kind. A **contact also still takes the old single `linkedin_url`** (`api/routers/companies.py:282-287`). - **Link kinds.** A company is offered social profiles only, its website being the `Company.website` column. Contacts and the profile also get code repositories and websites. This difference may be deliberate, but it is not written down. ### Differences that are on purpose (keep, and document) - **Phone uniqueness.** Only the profile's numbers are unique across the instance and count against the collision rate limit, because a person's number identifies them (`identifies_owner`, `phone_numbers.py:138, 284`). - **Account recovery.** *Gets me back in* is offered only on the profile, and only when a text gateway is installed. ## What would change 1. **The same controls everywhere.** The star for primary, the bin that removes at once, the help mark (#918) and the section layout (#919, #931) work the same way for a company, a contact and the profile. One code path builds the formsets, so the profile no longer has its own `get_numbers` / `get_addresses`. 2. **The same rules everywhere.** Use the same extension advice, the same autofill rule, and the same map choice (#923). For a company, either give it the choice like the others, or write down why it is always drawn. Apply #932 so each owner gets the right service prefix. 3. **The same display.** A contact's card shows its addresses as a company's page does, subject to the person's privacy choice. 4. **The same vCard.** Companies export and import their numbers, addresses and social profiles. The profile's "printed primary only" is a choice about what a CV shows, so it should be kept apart from what the vCard holds, and it should be decided and written down. 5. **The same API.** Contacts get `phone_numbers`, `postal_addresses` and `web_links` lists like companies. The single `phone` / `phone_country` and `linkedin_url` fields are deprecated and kept as aliases for a release. 6. **Write the deliberate differences down.** Uniqueness, recovery, and which link kinds a company gets go in one place: the docstrings of the shared models and the wiki's page on contact details. A test pins each one, so a difference can't come back unnoticed. ## Related #918, #919, #922, #923, #925, #931, #932.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#934
No description provided.