Put companies, departments and the person's card on the plugin surface #1020

Merged
tiagoagueda merged 1 commit from 328-plugin-surface-companies into main 2026-10-20 12:00:04 +00:00
Owner

Closes #328

What changed

  • postulo.plugins.api now hands out Company, Department, CompanyKind, PhoneNumberKind, PostalAddressKind, the plain row values PhoneRow / AddressRow / LinkRow with numbers_for / addresses_for / links_for (which follow the person's switches), and the person's card (PersonCard, person_card(user), built beside contact_details).
  • New services in jobs/surface.py, all with a keyword actor (and via for the trail): update_company, get_or_create_department, create_contact, update_contact, save_phone_numbers, save_postal_addresses, save_web_links. A refusal is a WriteRefused with the reason; a row the rules refuse comes back in SavedRows.refused. With a switch off only the primary is written and hidden rows are left alone.
  • The API's add_contact goes through create_contact and now needs write access to the company (it needed only read before: a bug fixed on the way; the sharing sweep says so).
  • Wiki: rows in Writing a plugin (committed in the wiki repo, not pushed).

The four accepted decisions

  1. The person's own details are read only: nothing on the surface writes a User or a Profile, and the rows calls refuse a profile as a holder.
  2. The card carries the sign-in address (User.email).
  3. actor is accepted by every write; it is recorded where a trail already exists (company and contact, via jobs/trail.py), nowhere else.
  4. While employer structure is off, reads work but a new department, a contact put in one and a parent are refused.

Library check

None needed.

Model use

Written with Claude Code, reviewed by the person who opens the PR.

🤖 Generated with Claude Code

Closes #328 ## What changed - `postulo.plugins.api` now hands out `Company`, `Department`, `CompanyKind`, `PhoneNumberKind`, `PostalAddressKind`, the plain row values `PhoneRow` / `AddressRow` / `LinkRow` with `numbers_for` / `addresses_for` / `links_for` (which follow the person's switches), and the person's card (`PersonCard`, `person_card(user)`, built beside `contact_details`). - New services in `jobs/surface.py`, all with a keyword `actor` (and `via` for the trail): `update_company`, `get_or_create_department`, `create_contact`, `update_contact`, `save_phone_numbers`, `save_postal_addresses`, `save_web_links`. A refusal is a `WriteRefused` with the reason; a row the rules refuse comes back in `SavedRows.refused`. With a switch off only the primary is written and hidden rows are left alone. - The API's `add_contact` goes through `create_contact` and now needs write access to the company (it needed only read before: a bug fixed on the way; the sharing sweep says so). - Wiki: rows in *Writing a plugin* (committed in the wiki repo, not pushed). ## The four accepted decisions 1. The person's own details are read only: nothing on the surface writes a `User` or a `Profile`, and the rows calls refuse a profile as a holder. 2. The card carries the sign-in address (`User.email`). 3. `actor` is accepted by every write; it is recorded where a trail already exists (company and contact, via `jobs/trail.py`), nowhere else. 4. While *employer structure* is off, reads work but a new department, a contact put in one and a parent are refused. ## Library check None needed. ## Model use Written with Claude Code, reviewed by the person who opens the PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Put companies, departments and the person's card on the plugin surface (#328)
Some checks failed
CI / Browser tests (Chromium) (pull_request) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (pull_request) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (pull_request) Has been cancelled
CI / Security audit (pull_request) Has been cancelled
CI / Browser tests (Chromium) (push) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (push) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (push) Has been cancelled
CI / Database tests on PostgreSQL (push) Has been cancelled
CI / Security audit (push) Has been cancelled
Every Python / Unit tests (Python 3.12) (push) Has been cancelled
Every Python / Unit tests (Python 3.13) (push) Has been cancelled
de5bfaba52
A sync of contacts (postulo-dav#8-#10) could not be written against the surface:
Company and Department were not on it, nothing wrote a company's details, and a
contact was made by saving the model, which the wiki tells plugins never to do. The
API's add_contact did the same, and needed only read access to the company.

The surface now holds the two records and their vocabularies, a holder's numbers,
addresses and links as plain values that follow the person's switches, and the
person's own card (given and family name, sign-in address, headline, location, kind
of picture, rows), built beside contact_details. The card is read only: nothing on
the surface writes a User or a Profile, and the rows calls refuse a profile.

The writes live in jobs/surface.py: update_company, get_or_create_department,
create_contact, update_contact, save_phone_numbers, save_postal_addresses and
save_web_links. Each takes an actor (the account the work is done for, checked
against a write share), a record that is not the actor's to change is refused, a
refusal is a WriteRefused with the reason, and a row the rules refuse comes back in
SavedRows.refused instead of raising. With a switch off only the primary is written
and the hidden rows are left alone. While employer structure is off, a new
department, a contact in one and a parent are refused. The company and contact trail
records the change under the plugin's name.

add_contact in the API now goes through create_contact and needs write access to the
company; the sharing sweep says so.

Closes #328

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tiagoagueda force-pushed 328-plugin-surface-companies from de5bfaba52
Some checks failed
CI / Browser tests (Chromium) (pull_request) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (pull_request) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (pull_request) Has been cancelled
CI / Security audit (pull_request) Has been cancelled
CI / Browser tests (Chromium) (push) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (push) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (push) Has been cancelled
CI / Database tests on PostgreSQL (push) Has been cancelled
CI / Security audit (push) Has been cancelled
Every Python / Unit tests (Python 3.12) (push) Has been cancelled
Every Python / Unit tests (Python 3.13) (push) Has been cancelled
to ad063ece46
All checks were successful
CI / Unit tests and coverage (Python 3.14) (push) Successful in 11m57s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m28s
CI / Database tests on PostgreSQL (push) Successful in 3m17s
CI / Security audit (push) Successful in 40s
CI / Browser tests (Chromium) (push) Successful in 28m54s
Every Python / Unit tests (Python 3.12) (push) Successful in 12m20s
Every Python / Unit tests (Python 3.13) (push) Successful in 12m6s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m12s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m30s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m17s
CI / Security audit (pull_request) Successful in 41s
CI / Browser tests (Chromium) (pull_request) Successful in 28m43s
2026-10-19 19:26:47 +00:00
Compare
tiagoagueda force-pushed 328-plugin-surface-companies from ad063ece46
All checks were successful
CI / Unit tests and coverage (Python 3.14) (push) Successful in 11m57s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m28s
CI / Database tests on PostgreSQL (push) Successful in 3m17s
CI / Security audit (push) Successful in 40s
CI / Browser tests (Chromium) (push) Successful in 28m54s
Every Python / Unit tests (Python 3.12) (push) Successful in 12m20s
Every Python / Unit tests (Python 3.13) (push) Successful in 12m6s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m12s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m30s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m17s
CI / Security audit (pull_request) Successful in 41s
CI / Browser tests (Chromium) (pull_request) Successful in 28m43s
to 35883e5215
All checks were successful
CI / Unit tests and coverage (Python 3.14) (push) Successful in 12m17s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m43s
CI / Database tests on PostgreSQL (push) Successful in 3m42s
CI / Security audit (push) Successful in 49s
CI / Browser tests (Chromium) (push) Successful in 29m12s
Every Python / Unit tests (Python 3.12) (push) Successful in 13m4s
Every Python / Unit tests (Python 3.13) (push) Successful in 13m22s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m34s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m46s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m56s
CI / Security audit (pull_request) Successful in 55s
CI / Browser tests (Chromium) (pull_request) Successful in 29m30s
2026-10-20 06:22:50 +00:00
Compare
tiagoagueda deleted branch 328-plugin-surface-companies 2026-10-20 12:00:06 +00:00
Sign in to join this conversation.
No reviewers
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!1020
No description provided.