Skip to content

refactor(organisations): delete StackiqService's unused organisation sync, fix the contact backup entity (REVIEW) - #1412

Merged
rubenvdlinde merged 14 commits into
developmentfrom
fix/stackiq-organisation-creation
Nov 3, 2026
Merged

rubenvdlinde merged 14 commits into
developmentfrom
fix/stackiq-organisation-creation

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

REVIEW (organisation ownership and membership). Held. This PR is stacked on #1404 and #1405. Their commits leave this diff as soon as they merge.

1. StackiqService's organisation sync and creation are deleted (no production caller)

RW118 found that the changed StackiqService methods have no production caller. I confirmed it:

  • The background job creates organisations through OrganisatieService::createOrganisationInOpenRegister(), called from OrganizationSyncService.
  • handleNewOrganization and handleOrganizationUpdate exist on Stackiq\OrganizationHandler, which is the class that is used.
  • No class in lib/, src/ or appinfo/ references StackiqService at all. Only comments name it.

So instead of fixing a path nothing runs, this PR deletes it:

  • the entry points handleNewOrganization, handleOrganizationUpdate, syncOrganizationWithOpenRegister and createOrganisationInOpenRegister, plus the 16 private methods only those reach (1,800 lines, computed as the closure of callers);
  • the five constructor dependencies that were never read afterwards (OrganizationHandler, SymfonyEmailService, IUserSession, IUserManager, IGroupManager).

This removes the problem RW118 found: the first version of this PR called OpenRegister's createOrganisation() without checking the slug, and on a slug collision OrganisationMapper::save() updates the existing organisation, which would have handed it to the caller. It also removes the original bugs (createWithUuid does not exist, getUID() on null). RW118's low items (a non-string contact e-mail, an empty e-mail added as a member, the golden double that ignored the organization filter) were all in the deleted code and its golden.

The golden test of the deleted path (StackiqServiceOrganisationSyncGoldenTest), the stub additions it needed, and the stale tests/OrganizationSyncTest.php go too. That file sits outside every test suite and uses the old constructor.

What stays in StackiqService (contact person processing, user groups, hierarchy) is unchanged. Its remaining tests pass: StackiqServiceDecompositionTest and StackiqServiceOrganisationMapperTest.

Not in this PR, for a decision: the whole StackiqService class (1,406 lines left) is unreferenced. Deleting it is a separate, larger PR.

Related, production path: OrganizationSyncService deliberately adopts an existing entity with the same slug (adoptOrganisationEntityBySlug). ORX8 is fixing the slug overwrite in OpenRegister itself.

2. ContactpersoonService backup entity (inherited, the same on both trees)

ensureOrganisationEntityPublic(orgObject: ...) used a named parameter that does not exist (the parameter is organisationObject). The Error escaped catch (\Exception), so creating the backup entity for a new contact person of an active organisation without an entity always failed. The fix renames the argument. sendEmails keeps its default (true), as in the other two backup paths in OrganizationSyncService.

  • ContactpersoonBackupEntityTest failed on the old code with Error: Unknown named parameter $orgObject.
  • Mutants 2 of 2 killed (the old argument name, the status check dropped).

3. RW93 follow-ups for #1404 and #1405

Numbers (PHP, lizard, against development plus #1404 and #1405)

base this PR
unit LOC 41,149 39,432
size > 60 32.53% 30.02%
McCabe > 10 16.85% 16.06%
params >= 3 14.97% 15.60%
params >= 5 3.13% 3.25%

The parameter shares rise only because 1,717 mostly small-parameter lines leave the denominator. No unit gained parameters. #1413 (listener) takes params >= 3 down by about 1.1 points.

Verified locally

  • Diff-scoped checks on the touched lib files: php -l, phpcs, phpstan, psalm and phpmd are clean.
  • The touched and affected tests pass, including StackiqService's remaining tests, the organisation sync goldens and the koppelingen golden.
  • development (ea0dcc2) is merged in, without a rebase.
  • The full unit suite ran on the previous head of this branch: exit 0, 1645 tests. A re-run on the deletion and the hydra gates are queued, and their results follow in a comment.

Assisted-by: ClaudeCode:claude-opus-5-5

…acting organisation pinned

Assisted-by: ClaudeCode:claude-opus-5-5
…to access, query and search steps

getKoppelingenGebruikByUuid keeps its signature and its error envelope; the work
moves into koppelingenGebruik, mayReadKoppelingen (ambtenaar, or the active
organisation owns the object), searchKoppelingen, isOrganisationUuid and
emptyKoppelingenPage, each with at most two parameters. Order of the lookups,
the log lines and the results are unchanged (golden of the previous commit).

Assisted-by: ClaudeCode:claude-opus-5-5
…to find, fetch and process steps

processRelatedContactPersons drops its unused organisation object parameter and
hands the work to findRelatedContacts, contactsFromOrganisationList,
fetchListedContact, processRelatedContact and completeRelatedContact, each with
at most two parameters (the sync run array, stats held by reference). Lookups,
saves, log lines and stats are unchanged (golden of the previous commit).

Assisted-by: ClaudeCode:claude-opus-5-5
…of an organisatie object

The OrganisationMapper stub gains findBySlug, as on OpenRegister's mapper.

Assisted-by: ClaudeCode:claude-opus-5-5
…nto refresh, adopt-by-slug and create steps

ensureOrganisationEntity takes the organisatie object and one run array (stats
held by reference, and an explicit sendEmails at every call site) and hands
the work to fullOrganisationObject, refreshOrganisationEntity,
adoptOrganisationEntityBySlug, createOrganisationEntity, mailOrganisation and
makeEntityOwner. The public wrapper keeps its signature. Lookups, saves,
mails, log lines and stats are unchanged (golden of the previous commit).

Assisted-by: ClaudeCode:claude-opus-5-5
… it behaves today

Creating an organisation escapes with an Error on both paths: the signed-in
path calls OrganisationMapper::createWithUuid, which OpenRegister does not
have, and the anonymous path saves and then reads the absent user.

Assisted-by: ClaudeCode:claude-opus-5-5
…m the OpenRegister create and update steps

Neither createOrganisationInOpenRegisterInternal nor updateOrganisationInOpenRegister
reads the OrganisationService it was handed. The public create wrapper still
fetches it first, so a missing OpenRegister fails at the same point. The golden
masks mock and anonymous class names, which depend on test order; it was
re-recorded on the code before this commit.

Assisted-by: ClaudeCode:claude-opus-5-5
…contacts' and 'origin/refactor/sig-koppelingen-gebruik-params' into fix/stackiq-organisation-creation
…ationService

StackiqService created a signed-in user's organisation with
OrganisationMapper::createWithUuid(), which OpenRegister does not have, and
an anonymous one by saving an admin-owned entity and then calling getUID() on
the null user. Both ended in an Error that escaped catch (\Exception), so
creation never returned an organisation.

A signed-in creation now goes through OrganisationService::createOrganisation()
with addCurrentUser, so the creator becomes owner and member, and the
organisation's contact persons are added with addUserToOrganisation(). Without
a signed-in user nothing is created: the specs do not say who would own it.

ContactpersoonService passed orgObject: to ensureOrganisationEntityPublic(),
whose parameter is organisationObject, so its backup entity creation always
failed with an Error.

Tests: golden cases recorded on the old code for the anonymous, forged owner
and member, creator-as-contact and missing-service paths (the Organisation
stub answers magic accessors like the Entity does, so the anonymous case
reaches the save it really makes); the backfill sends no mail and
processSpecificOrganization mails on create; the koppelingen lookup cases for
a forged owner filter, owner lookalikes and forged @self/isAmbtenaar options.

Assisted-by: ClaudeCode:claude-opus-5-5
@rubenvdlinde
rubenvdlinde marked this pull request as ready for review November 3, 2026 17:37
…sync and creation

Nothing calls StackiqService::handleNewOrganization, handleOrganizationUpdate,
syncOrganizationWithOpenRegister or createOrganisationInOpenRegister: the
background job creates organisations through OrganisatieService, and no class
in lib/, src/ or appinfo/ references StackiqService at all. The 20 methods only
those reach (1,800 lines) go, and with them the never-read OrganizationHandler,
SymfonyEmailService, IUserSession, IUserManager and IGroupManager
dependencies.

That removes the creation path that called OpenRegister's createOrganisation()
without a slug check, which would have handed an existing organisation with the
same name to the caller, instead of fixing a path nothing uses. The golden test
for that path, its stub additions and the stale tests/OrganizationSyncTest.php
(outside every suite, against the old constructor) go too.

Assisted-by: ClaudeCode:claude-opus-5-5
@rubenvdlinde rubenvdlinde changed the title fix(organisations): create OpenRegister organisations through OrganisationService (REVIEW) refactor(organisations): delete StackiqService's unused organisation sync, fix the contact backup entity (REVIEW) Nov 3, 2026
@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Rerun on the deletion head (37745e8): full unit suite (phpunit-unit.xml, pcov, --fail-on-risky) exit 0, 1630 tests, 0 failures, 0 risky, 59 skipped. Hydra gates --scope-to-diff --base origin/development: 37 of 37 applicable gates ran, 0 FAIL.

@rubenvdlinde
rubenvdlinde merged commit 3c9d91c into development Nov 3, 2026
39 of 42 checks passed
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/stackiq @ 55b1d50

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-manifest ✅
check-vue-demi ✅
test-l10n ✅
format ✅
check-schema-l10n ❌
check-l10n-js ✅
composer ✅ ✅ 127/127
npm ❌ ✅ 807/807
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ✅ 1610/1610 · 20 skipped
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-11-04 00:54 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant