feat(jobs): a company's legal state and dated hiring signals #1023

Merged
tiagoagueda merged 5 commits from 941-company-status into main 2026-10-22 01:06:25 +00:00
Owner

Closes #941

Builds on PR #1019 (#921) and must be merged after it. Until then this diff also shows the #921 commit (it took archive format 74 and accounts migration 0047; this takes format 75 and jobs migration 0041).

What changed

  • Company gains legal_state (active, administration, acquired, dissolved), legal_state_since, legal_state_source and acquired_by (checked like parent: not itself, no loop, visible to everybody the company is shared with).
  • New CompanyHiringSignal: dated entries (hiring, freeze, laying off) with a source, an author and the day; the history stays on the page. Older than 90 days (SIGNAL_STALE_AFTER_DAYS) reads "as of ".
  • "N open listings" is derived (listings the reader can see that are not closed or past closing, from the same shut_condition the tabs use) and stored nowhere.
  • UI: badge by the name, history on the company page, Status box and hiring entries on the edit page, Status column with Hiring and Exclude dissolved filters, map pin style and title, a quiet notice on listing and application pages, and a confirmation box when adding a listing (add form, capture review, application intake) for a dissolved company.
  • Wikidata P576 / P1366 are read and only offered on the review page; nothing changes until the person chooses.
  • Merge, copy-on-leaving, ownership moves, archive (format 75), importer (older formats, loop-safe), API (fields, POST/DELETE /companies/{id}/signals) and a vCard note for non-active states.

Decisions (from the maintainer)

Legal state and signals are shared with the company; each entry carries author and date; a write share may add/edit (delete, owner and share stay with managers); neutral factual labels; new strings are empty slots for Weblate.

Notes

  • The companies spreadsheet-style file (kind_files) goes to format 5 because it borrows the archive's block; it does not write the new fields yet.
  • Archive authors are usernames: restored only for the importing person's own entries.
  • The Status column is on by default so its filters have a header to live in.

Library check

None needed.

Model use

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

🤖 Generated with Claude Code

Closes #941 **Builds on PR #1019 (#921) and must be merged after it.** Until then this diff also shows the #921 commit (it took archive format 74 and accounts migration 0047; this takes format 75 and jobs migration 0041). ## What changed - `Company` gains `legal_state` (active, administration, acquired, dissolved), `legal_state_since`, `legal_state_source` and `acquired_by` (checked like `parent`: not itself, no loop, visible to everybody the company is shared with). - New `CompanyHiringSignal`: dated entries (hiring, freeze, laying off) with a source, an author and the day; the history stays on the page. Older than 90 days (`SIGNAL_STALE_AFTER_DAYS`) reads "as of <date>". - "N open listings" is derived (listings the reader can see that are not closed or past closing, from the same `shut_condition` the tabs use) and stored nowhere. - UI: badge by the name, history on the company page, Status box and hiring entries on the edit page, Status column with *Hiring* and *Exclude dissolved* filters, map pin style and title, a quiet notice on listing and application pages, and a confirmation box when adding a listing (add form, capture review, application intake) for a dissolved company. - Wikidata P576 / P1366 are read and only offered on the review page; nothing changes until the person chooses. - Merge, copy-on-leaving, ownership moves, archive (format 75), importer (older formats, loop-safe), API (fields, `POST/DELETE /companies/{id}/signals`) and a vCard note for non-active states. ## Decisions (from the maintainer) Legal state and signals are shared with the company; each entry carries author and date; a write share may add/edit (delete, owner and share stay with managers); neutral factual labels; new strings are empty slots for Weblate. ## Notes - The companies spreadsheet-style file (`kind_files`) goes to format 5 because it borrows the archive's block; it does not write the new fields yet. - Archive authors are usernames: restored only for the importing person's own entries. - The *Status* column is on by default so its filters have a header to live in. ## 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)
feat(accounts): store form of address, pronouns and gender as concepts
Some checks failed
CI / Unit tests and coverage (Python 3.14) (push) Successful in 11m22s
CI / Security audit (push) Successful in 36s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m38s
CI / Database tests on PostgreSQL (push) Successful in 3m9s
Every Python / Unit tests (Python 3.12) (push) Successful in 11m32s
CI / Browser tests (Chromium) (push) Failing after 46m46s
Every Python / Unit tests (Python 3.13) (push) Successful in 11m17s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m4s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m36s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m21s
CI / Security audit (pull_request) Successful in 38s
CI / Browser tests (Chromium) (pull_request) Failing after 29m6s
90504be79a
A profile held one language's words ("Mme", "iel", "Féminin"), so a record
switched from English to French kept showing the English word, and a CV in
another language printed the words of the language they were typed in. The
maintainer decided (2026-10-15) to store language-neutral concepts and draw
them in the language of the page or document, with "Other" the one exception:
free text stored and shown as typed.

- accounts/addressing.py: concept keys (mr, doctor, doctor_f, she, he, they,
  female, non_binary ...), per-language word tables with their sources, the
  subset each language offers, label(field, key, language) (table, then the
  catalogues for a language or concept the tables lack) and concept_of /
  stored_value (a word of any language, or a pre-#864 gender word, is the
  concept; anything else stays as typed).
- Gendered titles have a neutral fallback: English and French print Dr and
  Prof. for both doctor and doctor_f; Portuguese prints Dr./Dr.ª; a bare Dr or
  Prof. becomes the unmarked concept.
- Migration accounts 0047 turns existing values into concepts (frozen table,
  unmatched text kept), reversible to the word of the record's language.
- The form offers (key, label) options, keeps a posted value on a bound form,
  and stores a word typed under Other as its concept. Documents and the vCard
  PRONOUNS line resolve the label in their own language; the profile API
  returns {key, label} and accepts a key, a word or free text.
- Archive format 74 and candidate format 22; the importer and the candidate
  reader map older words to concepts.
- Gender never decides pronouns or form of address, or the reverse.

Closes #921

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
feat(jobs): give a company a legal state and dated hiring signals
All checks were successful
CI / Unit tests and coverage (Python 3.14) (push) Successful in 12m11s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m40s
CI / Database tests on PostgreSQL (push) Successful in 3m23s
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 12m21s
Every Python / Unit tests (Python 3.13) (push) Successful in 13m3s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m51s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 3m2s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m50s
CI / Security audit (pull_request) Successful in 50s
CI / Browser tests (Chromium) (pull_request) Successful in 30m33s
1432d43ffc
Somebody looking for work could not see whether a company is still there, or whether it
is hiring, freezing or laying people off. A company now has two things, kept apart:

- a legal state (active, in administration, acquired, dissolved) with the day it became
  true, a source address and, for an acquired company, the company it became part of;
- dated hiring entries (hiring, freeze, laying off), each with its day, a source and who
  noted it, kept as a history. One older than 90 days reads "as of <date>".

Both are shared with the company: anyone who can see it sees them, and a write share may
add or change them (delete, owner and share stay with the managers). "N open listings" is
suggested from listings that have not closed and is stored nowhere.

Shown by the company's name, as a Status column with Hiring and Exclude dissolved filters,
on the map (pin style and title), and as a quiet notice on a dissolved, in-administration
or laying-off company's listings and applications; adding a listing for a dissolved company
asks for confirmation. Wikidata's end date (P576) and replacement (P1366) are only offered
on the review page. Merge, copy-on-leaving, the archive (format 75), the importer, the API
and the vCard note carry it.

Closes #941

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge main into 941-company-status
Some checks failed
CI / Unit tests and coverage (Python 3.14) (push) Successful in 12m46s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m41s
CI / Database tests on PostgreSQL (push) Successful in 3m50s
CI / Security audit (push) Failing after 51s
CI / Browser tests (Chromium) (push) Failing after 30m44s
Every Python / Unit tests (Python 3.12) (push) Successful in 12m25s
Every Python / Unit tests (Python 3.13) (push) Successful in 13m41s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m23s
CI / Browser tests (Chromium) (pull_request) Successful in 29m30s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m22s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 3m2s
CI / Security audit (pull_request) Failing after 58s
574bf825ef
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into HEAD
Some checks failed
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m43s
CI / Browser tests (Chromium) (pull_request) Successful in 46m43s
CI / Security audit (pull_request) Successful in 1m1s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m12s
Every Python / Unit tests (Python 3.13) (push) Successful in 29m52s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 2m26s
CI / Security audit (push) Successful in 1m5s
CI / Unit tests and coverage (Python 3.14) (push) Successful in 14m23s
CI / Database tests on PostgreSQL (push) Successful in 3m47s
Every Python / Unit tests (Python 3.12) (push) Failing after 33m59s
CI / Browser tests (Chromium) (push) Failing after 1h9m28s
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m25s
40422b7ea0
Merge main into 941-company-status
Some checks failed
CI / Unit tests and coverage (Python 3.14) (push) Has been cancelled
CI / Browser tests (Chromium) (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
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
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
3ce1894c6d
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tiagoagueda deleted branch 941-company-status 2026-10-22 01:06:30 +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!1023
No description provided.