Skip to main content

Career plan — technical guide

This implementation reference covers the employee career-project list/details, target jobs and horizons, manager-profile coexistence and downstream consumers. It is intended for implementation and support teams. See functional for the user journey and configuration for rollout.

Architecture and responsibilities​

Verified in code: the observed flow belongs to smartmobility-legacy, implemented in Symfony controllers, Twig templates, browser forms/AJAX and Doctrine entities. The modern application shell does not make this a nextgen-native feature.

The diagram combines checked call sites and data dependencies; it does not claim a measured event-delivery latency or an immediate AIHub refresh.

ComponentResponsibility
Customer/InternalJobBundle/Controller/UserPlanController.phpStandalone list/detail, create/rename/delete, target add/edit/delete and action handlers.
Customer/InternalJobBundle/Resources/views/Default/{list.carierplan,career-plan-details}.html.twig and PartialsCards, dialogs, inline rename, empty/full states and browser submissions.
Company/ProspectBundle/Services/ViewSection/CareerPlanSection.phpManager career visibility and employee plan/matching data.
Main/ConfigBundle/Controller/UserPlanController.phpDistinct shared manager/accompaniment handlers; not interchangeable with standalone employee handlers.
Company/AccountBundle/Controller/SettingController.php::mycareerActionCompany target intervals and selected matching values.
Main/ConfigBundle/Entity/UserPlanJob.phpJob/horizon association and matching lookup.
EventSubscriber/{JobOfferMatchingV1ScoreSubscriber,PlatformActivitySubscriber}.phpMatching recalculation triggers and activity messaging for registered plan events.

Paths in the table are relative to smartmobility-legacy/src.

Request and data flow​

Verified in code: the employee list uses the authenticated user, retrieves their ordered plans, computes the next main/B/C slot, and renders the legacy template. Detail checks plan ownership. Both list and detail redirect to internal-job home when my_career_v2 is disabled. The submenu independently checks careerplan_fo.

Creation validates a session CSRF token, user, nonempty name/type and fewer than three plans, persists timestamps and ownership, flushes, dispatches a creation event under the UserPlanChangedEvent::class event name, then redirects to detail. UserPlan::setType normalizes unsupported type values to the principal type. The model's maximum is MAX_PLANS = 3.

Adding a target requires an AJAX request and valid CSRF token, finds the plan by ID and current user, resolves job and horizon, rejects an already-used horizon or full plan, persists the association and parent update time, and dispatches UserPlanJobChangedEvent. UI refresh is asynchronous; verify the refreshed count/card before repeating a submission.

Observed in application: the prescribed test account created plan B and persisted a Data Manager target. The refreshed screen displayed one target, 0–0 years and 41% matching.

Full Chrome window after a successful plan-target save

The reopened plan shows one target position and a Data Manager card with its horizon and match percentage. Check the target in the intended scenario before adding another; the match is not action completion.

View full size

Employee route contracts​

Verified in code: base path /plateforme/internal-job, from config/routes.yaml and bundle Resources/config/routing.yml. These are session-backed web routes, not a published nextgen REST API. Inputs below describe the checked handlers, not recommended direct calls.

Method and relative pathInputsTypical outcome
GET /career-projectsSession userHTML list; route has no method restriction in its declaration.
GET /career-projects/{id}Plan IDHTML detail; non-owner redirects to list.
POST /userplanname, type, _csrf_tokenRedirect to created detail or list with flash error.
PUT /userplanAJAX: id, name, _csrf_tokenJSON Career plan changed; 404 missing/foreign plan, 403 invalid token, 400 non-AJAX.
DELETE /userplanAJAX: idJSON Career plan deleted; 404 if no plan for the current user, 400 non-AJAX.
POST /userplan/jobtargetAJAX: id, job, target, _csrf_tokenJSON response: success; invalid inputs/capacity can redirect with flash errors; 403 invalid token, 400 non-AJAX.
POST /userplan/jobtarget/{id}job, targetRedirect after update; checks parent ownership.
DELETE /userplan/jobtargetAJAX: idJSON success/404/400; also removes matching rows for that job and the current user.
POST /userplan/actionid, description, dateCreates an incomplete dated action and redirects; ownership checked on create.
POST /userplan/action/{id}description, dateUpdates action and parent timestamp, then redirects.
POST /userplan/complete/actionAJAX: idSets completed/completed time; response text misleadingly says Career plan action deleted.

Sanitized logical target-add example: id=PLAN_ID&job=JOB_ID&target=HORIZON_ID&_csrf_token=SESSION_TOKEN. IDs must come from the authorized tenant/session UI; the token is session generated. Success example:

{"response": "success"}

Do not assume every response is JSON: several failures redirect, and the action-completion response text does not describe the actual state transition.

Data model and lifecycle​

Verified in code:

EntityStored relationships and state
UserPlanOwning user, type (Principal Plan, Plan B, Plan C), name, optional description/goal, manager-created flag, timestamps; one-to-many target jobs/actions with cascade all in mapping.
UserPlanJobPlan, job, nullable horizon, manager-created flag and timestamps.
UserPlanTargetCompany setting plus integer target_start/target_end and timestamps.
UserPlanActionPlan, optional name/description, due datetime, completed flag/time and timestamps.

The standalone create/rename input limits names to 30 characters in templates; the Doctrine name column is length 255, and the checked controllers do not impose the same explicit length check. Target uniqueness/capacity is an application-level check on add, not a demonstrated database constraint. The checked target edit handler does not repeat that capacity validation.

Action handlers parse d/m/Y and set due time to 23:59:59; invalid-date handling is not robustly established. The source contains action templates/handlers, but a visible employee action editor was Not verified in the current journey.

Matching display calls getUserPercentageMatching, reads UserJobMatching for the plan owner/job and rounds score * 100; a missing record returns zero. It differs from the separate older getUserPercentage skill-count calculation. No prediction of selection or promotion should be inferred from this number.

Removing a configured horizon clears existing association references before deleting that horizon. Recreating its interval does not reconnect those associations. The parameters handler processes matching switches before interval validation/transaction, so horizon rollback does not prove switch rollback.

Events and downstream consumers​

Verified in code: the standalone controller dispatches plan creation, plan deletion, target change and target removal events. JobOfferMatchingV1ScoreSubscriber subscribes to plan change, target change and target removal to calculate by user; PlatformActivitySubscriber registers plan activity events. The checked rename handler updates the database but does not itself dispatch UserPlanChangedEvent. Do not describe every mutation as emitting an identical event.

notifyManager in the standalone controller returns immediately before its mail code. Therefore automatic manager email is disabled in this checked implementation despite call sites and a notification template. Runtime delivery via other systems was Not verified.

Verified in code: AIHub's src/entity/user/imports/data_sources/import_users_sm.py joins UserPlan → UserPlanJob → job, requires a non-null job code, groups career-plan job codes/titles and maps codes to careerPlanJobReferences. src/entity/user/postgres/{models,repository}.py also carries career-plan references. This is downstream user data, not evidence of AIHub owning plan CRUD. Import frequency, event-to-import coupling and test-target propagation were Not verified.

Permissions, security and privacy​

Verified in code: employee detail, rename, plan deletion and target creation use ownership checks; create/rename/target-add validate CSRF. Manager career data is gated by bo_hide_carriere_tab and careerplan_bo. Parameters require a current-user school with no parent.

The inspected methods are inconsistent: target deletion, action edit/completion and shared manager handlers do not show equivalent ownership/CSRF checks inside each method. Global routing/firewall/voter protections were not fully audited. Do not generalize one handler's checks into a universal authorization guarantee. Review each mutation's full security chain before extending it; keep testing restricted to authorized test objects.

Career interests and user-linked matching are employee data. Keep logs/screenshots limited to authorized users and omit session tokens, private profile details and infrastructure addresses from client-facing documentation. This walkthrough stayed in the prescribed test account and company.

Legacy and redesigned coexistence​

The standalone internal-job flow, older Customer/ProfileBundle/Controller/UserPlanController.php, galaxy handlers, and Main/ConfigBundle manager handlers coexist. They use different request fields and validation and must be traced separately when modifying an entry point. The shared manager paths under /company/accompagnment/userplan are distinct from employee routes.

Verified in code: nextgen-back src/Infrastructure/Doctrine/DBAL/Type/UserPlanType.php preserves the three stored types. Nextgen-front src/modules/SmartTrad/utils/lexico-fallbacks.ts supplies career-plan vocabulary, and review/development components contain attachment labels. A complete nextgen plan CRUD screen/API or migration timetable was Not verified by these findings.

Failure modes, observability and extension points​

Investigate missing tabs through switch/submenu resolution; redirects through my_career_v2; rejected saves through CSRF/session, owner lookup, posted job/horizon and capacity; missing percentages through matching records; missing horizons through configuration changes. Flash errors and web redirects may conceal details from a JSON-only client. Check application errors, dispatched activity and matching subscribers within authorized tooling; no runtime logs or queue traces were inspected.

Runtime dependencies are the legacy Symfony/Twig/Doctrine stack, configured company jobs/horizons, session and CSRF services, stored matching data, and enabled downstream event consumers. AIHub is a consumer of exported/imported career-job references. Extend the owning controller/template plus model and every affected entry point, preserving stored plan types and testing ownership, duplicate horizons and partial configuration failure. A deployment procedure or source/deployed version identity was Not verified.

Evidence and limitations​

Observed in application: PS Testing only, prescribed account only, 23 October 2026; list/details, creation, job search/horizon, persisted target, inline name editor, full-plan error, manager-space switch and denied parameters page. No deletion was performed.

Verified in code: component paths above; legacy Main/ConfigBundle/Resources/config/doctrine/UserPlan{,Job,Target,Action}.orm.yml, Main/ConfigBundle/Resources/config/routing/userplan.yml, Main/SitemapBundle/Enum/Customer/InternalJobTabsEnum.php, Service/Configuration/UserPlanTargetDefaultConfigurationService.php, Event/UserPlan*Event.php, and config/configuration/default_configuration.yml; nextgen and AIHub paths identified above.

Not verified: complete authorization audit, configuration save, action management, deletion, recommendation recalculation delivery/latency, notification delivery, AIHub synchronization, mobile behavior and production parity. No application repository was changed.

Functional-guide evidence​

These implementation and product references support the functional guide. Local source behavior does not establish which revision or optional features are deployed. Application observations remain distinct from code and documentary evidence.

  • Observed in application: PS Testing only, prescribed test account only; space switch, list/detail, plan B creation, empty state, job search, required horizon, saved target and matching display, full-plan validation and denied parameters access. Full-window screenshots retain the surrounding application context.
  • Verified in code: legacy Customer/InternalJobBundle/Controller/UserPlanController.php, related Resources/views/Default and Partials templates, Main/ConfigBundle/Entity/UserPlan.php, UserPlanJob.php, and Company/ProspectBundle/Services/ViewSection/CareerPlanSection.php (paths relative to smartmobility-legacy/src).
  • Not verified: deletion, changed target/name persistence, action completion, manager profile editing, configuration save, mobile behavior, downstream recommendation timing and deployed/source commit equivalence. The documentation test plan and its target remain available in PS Testing.

Journey verification — 26 October 2026​

Observed in application: PS Testing 2026, Jim Carter. Created isolated plan C 14989; selected MANAGER LEADER BUSINESS MANAGEMENT, observed missing-horizon validation, chose 0–0 ans, saved and reloaded to one target/81% matching. The original main plan was preserved. On 27 October 2026, after explicit user authorization, existing plan B 14965 was renamed from Documentation walkthrough to Documentation G-04 plan B. A full reload retained the new title; DATA MANAGER, its 0–0-year horizon and 41% matching display remained visible. This resolves the earlier approval blocker for the rename only. Galaxy-origin saving is a separate unverified case.

Last updated on 3 November 2026