Import and export — technical guide
This guide describes the legacy-only Import Center and Export Center, for engineers and integration/support teams. The functional guide documents observed usage; the configuration guide separates tenant setup from customer administration. Local source inspection establishes implementation, not production deployment or successful execution.
Architecture and boundaries
Verified in code: both centers use Symfony controllers, Twig/jQuery interfaces and Doctrine persistence in smartmobility-legacy. The import pipeline stages spreadsheet rows before applying business changes. Campaign export is a separate asynchronous path producing private workbook files and email download links. Import reports are generated by the ImportCenter bundle; they are not campaign exports.
No replacement center is established in the inspected nextgen frontend/backend or aihub searches. This scope does not exclude unrelated imports or exports elsewhere in those applications. There is no verified nextgen migration, routing proxy or AI processing contract for these centers.
Component responsibilities
All source paths below are relative to smartmobility-legacy/.
| Component | Responsibility |
|---|---|
config/routes.yaml | Mounts import routes at /company/import-center and export routes at /company/export-center. |
src/Company/ImportCenterBundle/Controller/DefaultController.php::indexAction | Access check, company upload history and upload type/campaign choices. |
…/Controller/ImportController.php | Upload, preview, import request, delete, staged cell edits and revalidation. |
…/Controller/MatchController.php::indexAction/updateAction | Type-specific mapping choices, company language/repository validation, required headers and validation-task submission. |
…/Adapters/AdapterFactory.php::getImporter | Selects one of 12 type adapters and injects its domain service. |
…/Adapters/AbstractAdapter.php::importFile/validateRow | Stores uploaded source and metadata, submits parsing, validates adapted row DTOs. |
…/Services/Imports/ | Creates/updates the appropriate domain data for each import family. |
…/Services/ImportCenterUploadService.php | History projection and Symfony state-machine transitions. |
…/Services/ExcelDataExportService.php | Invalid-row report and imported-row changes report. |
src/Command/ImportCenter/ImportCenter{ParseFile,ValidateRows,ImportRows}Command.php | Background parsing, validation and import execution. |
src/Company/ExportCenterBundle/Controller/DefaultController.php::indexAction | Authorizes entry, projects company campaign tables and eligible tabs. |
src/Company/ExportCenterBundle/Controller/ExportController.php | Dispatches background campaign generators and packages authorized files for download. |
src/Command/Export/ExportCenter1to1Command.php / ExportReviewsCommand.php | Campaign workbook generation, private media/File records and delivery email. |
… in ImportCenter rows means src/Company/ImportCenterBundle. Frontend behavior is in each bundle's Resources/views/js/ templates; these are server-rendered legacy pages, even when navigation is supplied by a newer shell.
Import request and data flow
Upload and parsing
ImportController::ImportFileAction expects an XMLHttpRequest multipart upload containing file, file-type, active-sheet, optional interview-choice and _csrf_token. It checks the import voter, validates the import_center_csrf_token token, checks AdapterFactory::IMPORT_TYPES, resolves the interview for Interview Answers and calls MediaService::isValidFile(..., MediaReference::SHEET_TYPE).
The adapter moves the source to the transient import area, persists an ImportCenterUpload scoped to the initiating user's setting, assigns PARSING and submits ImportCenterParseFileCommand::NAME through CommandTaskRepository::push. The upload success response means the file was staged, not imported.
The parser uses PhpSpreadsheet IOFactory::load. Worksheet positions are 1-based at the interface and converted to 0-based indexes. Invalid indexes fall back to index 0. Header collection stops at the first empty cell in row 1; data collection begins at row 2 and stops at the first entirely empty row. Date-formatted cells use their formatted values; other values are trimmed strings. This is not a streaming contract for arbitrary sparse workbooks.
Matching and validation
MatchController selects DTO fields and required indicators by import type. For eligible families it fetches company languages, public company repositories and visible Smart Editor fields. Interview answer fields are generated from the selected interview. Submitted mapping is stored as JSON destination field → source column letter, for example:
{
"jobs.title": "A",
"jobs.code": "B"
}
This illustrative mapping is not a complete runnable request: the real matching form also submits its company language/repository choices and the source column keys. Duplicate destination choices collapse into one associative-array key; they should not be treated as supported multi-column merging. Users matching requires the contacts acknowledgement. The controller checks a set of required mappings, while DTO validators/domain lookups provide additional row-level constraints.
Matching applies the workflow transition, sets VALIDATING and queues app:import-center:validate-rows. Row DTO adaptation and validation store validity/errors separately from domain persistence. A validated preview can therefore contain both valid and invalid staged rows.
Correction and execution
changeImportRowColumnValue accepts key, value, row, resolves an upload in the current setting, requires the validated workflow and an invalid staged row, then resolves the mapped source column and persists its replacement. The client invokes it on blur. Revalidation queues another validation command and returns the upload to the validating stage.
ImportAction checks the company upload and permitted workflow transition, sets IMPORTING and queues app:import-center:import-rows with upload ID and initiating user ID. ImportCenterImportRowsCommand::importRows reads valid rows in batches of 500, adapts and validates again, calls the domain adapter, records changes and marks successful rows imported. It flushes/clears between batches; some domain services also flush. This is not one atomic file-wide transaction. Errors can leave already-persisted changes, so replay requires reconciliation rather than an assumed rollback/idempotency guarantee.
Types and identity
AdapterFactory::IMPORT_TYPES supports skills, jobs, jobsOffers, jobsSkills, users, userCompanyHierarchicalRelationships, companyEntityGroupUsers, formations, formationsSkills, userCampaigns, userSkills and interviewAnswers through their ImportCenterUpload constants and adapter classes. Use the constant values in code; display labels are translated separately.
| Business identity | Verified lookup |
|---|---|
| User create/update | src/Repository/UserRepository.php::getUsersByUserRowQuery: setting + email, up to two matches to detect ambiguity. |
| Job create/update | src/Main/ConfigBundle/Repository/JobRepository.php::getJobsByJobRowQuery: setting + repository + code. |
| Skill create/update | src/Main/ConfigBundle/Repository/SkillRepository.php::getSkillsBySkillRowQuery: setting + repository + code. |
| Translation | Jobs/Skills services use the upload locale to resolve/update translations; locale is not part of the base job/skill identity query. |
UsersService, JobsService and SkillsService::persistImportCenterRowDto reject ambiguous matches. Relationship importers are separate services; do not infer that Jobs&Skills creates missing reference jobs and skills. UserRow also exposes optional active-state, role and login-message controls, so imports can have effects beyond profile text.
Routes and representative contracts
These describe browser-facing legacy contracts, not a supported public integration API. Do not invoke production mutations merely to test examples.
| Method / path | Controller contract |
|---|---|
GET /company/import-center/ | Company history and upload form. |
POST /company/import-center/upload | Multipart upload; AJAX, import grant and CSRF checks. Successful JSON includes status, fileUploaded, history data. |
GET / POST /company/import-center/match/{id} | Render / submit column matching and queue validation. |
GET /company/import-center/preview/{id} | Render validated company upload; otherwise redirect. |
POST /company/import-center/import/{id} | Queue import; {"message":"success"} on accepted transition, 404-style error otherwise. |
DELETE /company/import-center/import/{id} | Delete upload entity after AJAX and delete-voter checks; not domain rollback. |
POST /company/import-center/import/{id}/change-import-row-column-value | Persist one staged cell; {"success":true} on success. |
POST /company/import-center/import/{id}/revalidate-import-rows | Queue revalidation; {"success":true} on success. |
GET /company/import-center/import/{id}/export/{exportType} | invalid_rows requires validated workflow; imported_rows requires imported workflow. Returns JSON file information through SpreadsheetService::downloadToNav. |
GET /company/export-center/ | Eligible campaign tables. |
/company/export-center/export/exportsfiles | Export form endpoint; client sends POST/AJAX data[module] and data[campaigns][]. Routing YAML does not itself restrict the HTTP method. |
GET /company/export-center/export/download/ | Query files is a JSON array of generated File IDs; authorized sources packaged as ZIP. |
Illustrative campaign-export body, with synthetic IDs:
data[module]=1to1
data[campaigns][]=101
data[campaigns][]=102
Module keys are 1to1 and PR. Their helpers return different success shapes (message versus response), and exportDocumentsAction can return a generic success on an empty/unhandled request path. Do not use a success JSON or the pre-request UI modal as proof of completed generation.
Persistence and lifecycle
| Entity / source | Data retained |
|---|---|
src/Entity/ImportCenterUpload.php | Tenant setting, uploader, filename/size/type, timestamps, status/workflow, source and chosen headers, locale, repository and optional interview. |
src/Entity/ImportCenterUploadRow.php | Source row number/data, validation/errors, imported flag and recorded changes. Upload foreign key has database cascade delete. |
src/Entity/ImportCenterReferences.php | Additional per-upload references such as campaign type; upload relationship cascades on delete. |
src/Entity/ImportCenterUploadStatus.php | Separate processing status lookup. |
src/Entity/ImportCenterUploadLog.php, ImportCenterUploadUserAction.php | Processing/user-action evidence via the logger service. Retention policy not established here. |
Legacy File entity, export command writers | Generated private campaign files associated with a creator, later resolved for download. |
Status constants: 1 PARSING, 2 PARSED, 3 VALIDATING, 4 VALIDATED, 5 IMPORTING, 6 IMPORTED, 7 PARSING_FAILED, 8 VALIDATING_FAILED, 9 IMPORT_FAILED. Workflow state and status lookup are separate fields: ImportCenterUploadService delegates transition eligibility to Symfony's state machine; code explicitly updates status records.
ImportCenterUploadStatusRepository::ALLOW_DELETE_STATUSES controls where the interface offers deletion: parsed, validated, imported and the three failure statuses. ImportController::deleteAction removes the upload entity, not the imported business entities. The controller does not itself repeat that UI status allow-list check. No verified cleanup guarantee for the original physical file or automatic domain rollback follows from this operation.
Campaign export flow and file delivery
ExportCenterBundle/Controller/DefaultController::indexAction scopes the displayed interviews/reviews to the user's setting. It builds tabs only if feature/permission checks pass and campaigns exist. The jQuery table uses global search plus exact multi-value questionnaire/status filters; values within a filter use OR and different columns combine with AND. Select-all operates on rows({search:'applied'}) across the filtered table; bulk export gathers checked IDs from that same scope.
ExportController::exportDocumentsAction dispatches by module. export1to1 and exportPR launch background PHP console commands using the configured asynchronous runtime. This path does not use the Import Center task queue. The generators are export-center:export1to1 and exportCenter:exportreviews.
The command writers create XLSX workbooks in private media, persist legacy File metadata and send email download links. The button's CSV label does not describe the checked writers' output. The review command has its own handling of campaign/questionnaire structures; do not assume identical worksheets across modules or a lossless import/export round trip.
ExportController::exportDownloadAction decodes File IDs, resolves each File and includes it only if its creator's setting equals the current user's setting. It reads authorized files, builds a temporary ZIP, returns an attachment and removes the temporary archive after sending. This is a company-level creator-setting check, not a verified owner-only download policy. The source media files are not deleted by temporary ZIP cleanup.
Configuration, permissions and privacy
Verified in code: config/configuration/feature_module.yml declares baseline import_center_access=false and export_center_menu_display=true under Advanced Reporting. config/configuration/default_permission.yml defaults import and Data Management grants to false for the listed standard roles. Effective tenant overrides and BaseVoter/module evaluation determine runtime access; file defaults alone do not establish availability.
ImportCenterVoter::canView/canImport combines module access with the applicable school permission. VIEW, IMPORT and DELETE currently resolve to the same underlying rule. Export homepage entry checks export_center and export_center_menu_display; tab checks add the relevant interview/review feature and role.
Security boundaries must be reviewed per route. Upload explicitly checks CSRF; this must not be generalized into a claim that every mutation has equivalent protection. Most inspected import read/update paths scope upload lookup to the user's setting, but delete uses a direct ID lookup. Campaign dispatch helpers do not repeat the homepage's complete permission checks in the inspected controller. Additional global access controls may apply; their full coverage was not verified. These observations are implementation limitations for authorized engineering review, not customer usage procedures or a claim of an exploitable deployed issue.
Staged rows, changes reports and campaign workbooks can contain personal data. Restrict access, avoid logging payloads in support notes and use the agreed retention process. No retention period, link expiry, per-user file ownership rule or universal masking policy was established from this investigation.
Failure modes and observability
| Failure | Inspection point |
|---|---|
| Upload rejected | AJAX, voter, CSRF, type, interview and MediaService checks; browser MIME/size validation. Server source includes a TODO for additional upload validation. |
| Parsing failed or pending | Task consumption, transient source availability, workbook format/worksheet/header structure and PARSING_FAILED. |
| Validation failed | Adapter/DTO constraints, required mapping, reference lookups and VALIDATING_FAILED. |
| Import partially applied | IMPORT_FAILED, row isImported/changes, domain persistence and batch boundaries; reconcile before retry. |
| Process reports success despite failure | Import command catch handlers set failed status and dispatch PostCatchEvent; the inspected execute methods still return console SUCCESS. Check persisted state/events, not only exit code. |
| Export request accepted but no delivery | Background process, private media File creation and mail handling; frontend success modal is optimistic and does not track completion. |
| ZIP has no usable file | File existence, creator-setting check, media resolution/readability. No accepted paths returns an error response. |
The import logger records actions such as UPLOAD, MATCH, IMPORT and DELETE; command handlers dispatch scoped failure events. Exact monitoring dashboards, queue latency, retry policy, process timeouts and deployed alerts were not inspected.
Extension points and runtime dependencies
Adding an import type requires coordinated upload constants/type lists, an adapter conforming to the import interface, DTO field/required-field definitions and constraints, domain service behavior, matching options and preview/report compatibility. AdapterFactory dynamically builds the adapter class and injects a selected service; merely adding a dropdown label is insufficient. Keep tenant checks, privilege-changing fields and duplicate-identity behavior explicit.
Campaign export changes must keep module dispatch, command names, spreadsheet writers, File persistence and delivery/download contracts aligned. Runtime dependencies include Symfony/Doctrine and the configured task consumer for imports, PhpSpreadsheet, writable transient/private storage, background PHP execution for campaign exports, ZIP support and configured mail services. No external AI service was traced for these centers.
Evidence and limitations
- Verified in code, 16 October 2026: paths/symbols in the component, route and persistence tables above; repository identity queries;
src/Repository/ImportCenterUploadStatusRepository.php; both bundles'Resources/views/js/;src/Command/Export/ExportCenter1to1Command.php::importflow andExportReviewsCommand.php::saveAndSendFile;config/configuration/feature_module.ymlanddefault_permission.yml. Source paths are used instead of unverified remote links. - Observed in application: supplementary evidence in the functional guide, including current Settings entry points and locale limitations. No technical screenshots are included.
- Documented in Confluence: PS Users, Jobs, Skills, Job&Skills, version 2, updated 29 April 2025. These describe staged async processing and correction. They contain older first-sheet wording and inconsistent identity examples; the current parser and repository symbols support the behavior documented here.
- The supplied KBCT page returned 404. The supplied PS methodology (v7, 30 December 2025) and onboarding (v9, 18 February 2026) pages were read but did not establish center implementation contracts.
- Not verified: deployed revision, end-to-end processing, file delivery, complete global route authorization, every importer’s detailed update/blank-value semantics, retention/retry policies and any nextgen center. No source application repository, configuration or data 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:
smartmobility.io, 16 October 2026, English and French manager/admin views; import history, upload type list, worksheet default, conditional interview selector, export tabs and reversible campaign selection. Employee navigation was separately inspected. No upload, matching submission, row edit, import, deletion, campaign export or delivery email was executed. - Verified in code: legacy
src/Company/ImportCenterBundle/controllers, templates, adapters, DTOs and services;src/Command/ImportCenter/;src/Company/ExportCenterBundle/;src/Command/Export/ExportCenter1to1Command.phpandExportReviewsCommand.php. See the technical evidence map for exact symbols. - Documented in Confluence: Import Center for Users, Jobs, Skills and Job&Skills, all last updated 29 April 2025, version 2. They support staged imports, correction and reference-data preparation. Their old Data Management navigation differs from the observed Settings menu. Their first-sheet-only wording and inconsistent identity descriptions are superseded here by the checked parser and repository queries.
- Not verified: live preview/correction and failure screenshots, generated report contents in a real run, delivery timing, all type-specific validation rules and production deployment parity. Existing upload examples were not opened for publication because they contain tenant data. No equivalent nextgen center is documented.
- The supplied
KBCT:2136702980reference returned 404. The supplied PS methodology/onboarding pages were read but provide no center-specific configuration contract.
Filter combination and table behavior are Verified in code in Company/ImportCenterBundle/Resources/views/js/index.js.twig.
Last updated on 3 November 2026