User management — legacy technical guide
Architecture and boundaries
| Layer | Responsibilities / source |
|---|---|
| Legacy employee list | src/Company/ProspectBundle/Controller/DefaultController.php::listAction, Resources/views/Default/list.html.twig, company employee queries and configurable DataTable. Live route /company/accompagnement/list. |
| Legacy manual creation | src/Company/CandidateBundle/Controller/DefaultController.php::newAction, Resources/views/Default/new.html.twig, src/Main/ConfigBundle/Form/Type/ProspectNewType.php. Live route /company/candidate/new. The Prospect controller's separate newAction is not this observed creation surface. |
| Embedded nextgen list | Frontend src/modules/Users/User/user.routes.ts, User.vue, components/UserList.vue. Legacy src/Twig/Company/CompanyNavigationExtension.php::getSmrUsersPeopleFeature selects admin/personal feature path from Service/V2/SmrDisplayService. Observed manager path /company/space/admin/users/people, embedded /space/admin/users/people. |
| Nextgen API | src/Http/Api/Routing/V1/user.yaml, user_import.yaml; Domain/V1/User/DTO/UserDTO.php; User controller/transformer/handler/repository/listener. V1 routes mounted under /api/v1 by config/routes.yaml. |
| AIhub | Separate company-scoped extraction and standardization of user/job/skill data, not an established source of account roles or synchronous provisioning. |
New user list -> user.composables -> user.api -> /api/v1/user -> permission + company/visibility query
Create wizard -> POST user -> UserHandler creates identity + Customer
-> separate POST user/{id}/job/{jobId} -> assignment event -> consumer
Legacy creation -> Candidate controller + form -> user/related records -> creation subscriber
User create/update -> UserListener -> group-rule messages -> recalculation consumer
AIhub user import -> source extraction -> standardization (separate process)
The new list can return a user to a legacy detail route using the employee hash. This is explicit client navigation, not proof of automatic migration. Familiar shared entity/table names do not establish shared deployed storage, sync direction, SSO precedence or rollback.
Data model and identities
The nextgen Infrastructure/Persistence/Entity/User/ model distinguishes User, Customer (employee profile) and School (manager-related record). The legacy domain has equivalent concepts under Main/ConfigBundle. Do not treat a User ID, Customer hash, manager User ID and School ID as interchangeable.
UserDTO maps status to enabled, lastname to the persisted name, and employee profile hash through Customer. Manager ID in the API is a User ID; Infrastructure/Persistence/Transformer/V1/User/UserTransformer::reverseTransformManager resolves it through SchoolRepository::findByUser. It uses isset, so a null manager payload is not proof of an implemented detach operation. Job ID/titles are read-only user DTO properties; use the assignment endpoint.
Infrastructure/Persistence/Handler/User/UserHandler::finalizeEntityBeforeCreate generates username/timestamps, hashes credentials via UserPasswordService, and creates Customer::fromUser. Updates regenerate the display username. Entity/User/User::canonicalizeProperties canonicalizes email and username before persistence/update. User DTO uniqueness checks emailCanonical; it is not simply a tenant-visible list search.
Employee activation, job requirements/skills, role membership, visibility, anonymization and employee classification are separate state. Anonymized-user filtering is a separate concern; UserManager::loadIncludingAnonymized temporarily manages that filter for internal processing. This does not authorize general UI access to anonymized records.
Lifecycle and asynchronous work
Legacy CandidateBundle::newAction writes user and related records, establishes current/selected manager context, then dispatches UserEvent::CREATION. src/EventSubscriber/UserSubscriber::{onUserCreated,addAutoTestToUser,setHasToChangePassword} starts additional processing and dispatches UserProfileCreatedEvent. Do not assume manual creation is a side-effect-free identity insert or immediate completion of all downstream work.
Legacy ProspectBundle::activateDesactivateAction toggles enabled state and can rewrite the email with a deactivation marker; activation restores the original unless another account already uses it, producing a warning. The inspected method alone does not establish exhaustive authorization/tenant enforcement; its lookup should not be presented as proof of cross-company protection. No activation request was executed.
Nextgen UserHandler::handleDelete disables without deleting, confirmed by the delete feature's expected subsequent GET 200/status=false. This is distinct from anonymization. Persistence/Listener/User/UserListener publishes group-rule work after create/import-create and update/import-update. It records the old manager before update and republishes affected current/old manager work when changed. Command/Consumer/Rule/Group/UserGroupRuleUpdateConsumerCommand.php owns the corresponding rule-update processing; complete retry/latency semantics were not validated.
Http/Api/Controller/V1/User/UserController::assignJob dispatches an update event and publishes JobAssignedToUser. Command/Consumer/User/JobAssignedToUserConsumerCommand calls Infrastructure/Service/User/UserSkillService::addSkillsToUser to attach missing skills. It does not establish that job-required mastery overwrites assessments or that removal reverses all employee data.
Bulk status behavior
Frontend components/UserStatusBulkActions.vue loops selected IDs and calls useUserOperations::updateUserStatus, which returns false on an API failure. The loop does not require that boolean to be true before continuing to its success message; no atomic backend bulk operation is used there. Row changes are optimistic with a rollback attempt. Audit actual saved results and request failures; a toast is not a transaction receipt.
Provisioning and AIhub boundaries
Legacy src/Service/Imports/UserImportsService.php exposes user/customer/manager creation, job assignment, identity changes and relationship import helpers. The documentary Import Center sequence is distinct from nextgen /user/import metadata. Nextgen findUserByEmployeeIdOrEmail selects within company, using supplied employee ID or canonical email and prioritizing a match on both. Conflicts, empty-field semantics and row-level recovery need the actual import DTO/process contract; no universal overwrite promise is made here.
AIhub src/entity/user/imports/data_sources/import_users_sm.py::import_users_from_sm extracts company-filtered user identity, active flag, job/organizational data and related attributes; active-only extraction defaults true. Optional filters can use employee number, email or User ID depending on arguments. src/entity/user/imports/import_users_process.py::standardize_user_batch standardizes selected users. This is evidence of extraction/standardization, not a synchronous Save hook, role authority, immediate deactivation propagation or automatic access revocation in every downstream system.
Runtime, observability and extension points
Runtime dependencies include the API/security context, Doctrine persistence, legacy form/DataTable stack, embedded frontend messaging and the rule/assignment consumer runtime. External HR/SSO flows and AIhub are separate integrations whose scheduling and precedence were not verified.
| Symptom | Investigation point |
|---|---|
| List count differs by generation | Compare company, visibility, master/service-account exclusions, status and restored filters before diagnosing sync. |
| No result after filter | Current page, username versus email query, status and scoped population. |
| Creation error but user exists | Separate POST and job-assignment calls; inspect existing identity before retry. |
| Bulk success with unchanged account | Per-ID requests, helper false returns, saved status; no atomic completion guarantee. |
| Reactivation conflict | Legacy original-email collision; not equivalent to nextgen status PATCH. |
| Group/skill changes delayed | Published user/group/job work and consumer completion; no timing guarantee. |
| API failure | Route permission (403), DTO invalid-parameter response (400), scoped/missing resource (404) according to checked test contracts. |
Frontend catch paths log errors, but they are not an activity-audit trail. Log identifiers/status necessary for diagnosis rather than full email, address, birth date, profile or assessment payloads. Extend route → permission → DTO → transformer/handler → scoped repository → event/consumer and UI mapper together; do not add a field to the form alone and assume persistence/filtering exists.
Evidence, tests and limitations
Verified in code: named paths/symbols across the four repositories, inspected 15 October 2026. No application source changed, workers started or API mutation executed. Observed in application: UI routes and first-step validation only, documented in the functional/configuration guides.
Test sources inspected, not run: nextgen tests/Behat/Features/V1/user/{create,read,edit,delete,import,attach_job,access,anonymization}.feature were located; creation/deletion scenarios were read directly. They cover expected invalid/duplicate identities, status defaults, ignored job assignment during creation and deactivation-on-delete. The remaining located suites are pointers for future validation, not claimed passing checks.
Documented in Confluence: User Management System, v4, 12 June 2025 (intent, not proof of shipped bulk/export/custom-filter features); Import Center for Users, v2, 29 April 2025 (legacy documentary workflow, with title/code/deactivation inconsistencies); SMR playbook, v3, 24 September 2026 (coexistence and deployment intent).
Supplied KBCT 2136702980 was unavailable. Supplied PS methodology/onboarding entry pages were reviewed earlier in this session and do not establish user API contracts. Not verified: deployed database sharing, SSO conflict precedence, final create/edit behavior, full import semantics, consumer retry/delivery timing, audit completeness, session invalidation and account lifecycle propagation to external systems.
Dernière mise à jour : 3 novembre 2026