diff --git a/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js b/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js
index 5bdbec3a680..ecabb5e092f 100644
--- a/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js
+++ b/api/src/certification/enrolment/domain/usecases/add-candidate-to-session.js
@@ -101,7 +101,7 @@ export async function addCandidateToSession({
} catch {
throw new CertificationCandidatesError({
code: CERTIFICATION_CANDIDATES_ERRORS.CANDIDATE_RESULT_RECIPIENT_EMAIL_NOT_VALID.code,
- meta: { email: candidate.resultRecipientEmail },
+ meta: { value: candidate.resultRecipientEmail },
});
}
}
@@ -111,7 +111,7 @@ export async function addCandidateToSession({
} catch {
throw new CertificationCandidatesError({
code: CERTIFICATION_CANDIDATES_ERRORS.CANDIDATE_EMAIL_NOT_VALID.code,
- meta: { email: candidate.email },
+ meta: { value: candidate.email },
});
}
}
diff --git a/api/src/shared/application/error-manager.js b/api/src/shared/application/error-manager.js
index 2b6539a7312..64ce2838253 100644
--- a/api/src/shared/application/error-manager.js
+++ b/api/src/shared/application/error-manager.js
@@ -469,7 +469,7 @@ function _mapToHttpError(error) {
}
if (error instanceof DomainErrors.CertificationCandidateOnFinalizedSessionError) {
- return new HttpErrors.ForbiddenError(error.message);
+ return new HttpErrors.ForbiddenError(error.message, error.code);
}
if (error instanceof DomainErrors.OrganizationLearnerCannotBeDissociatedError) {
diff --git a/api/src/shared/domain/errors.js b/api/src/shared/domain/errors.js
index 40e29c6384b..0aa8f1c34f0 100644
--- a/api/src/shared/domain/errors.js
+++ b/api/src/shared/domain/errors.js
@@ -96,6 +96,7 @@ class CertificationEndedByFinalizationError extends DomainError {
class CertificationCandidateOnFinalizedSessionError extends DomainError {
constructor(message = "Cette session a déjà été finalisée, l'ajout de candidat n'est pas autorisé") {
super(message);
+ this.code = 'CANDIDATE_NOT_ALLOWED_FOR_FINALIZED_SESSION';
}
}
diff --git a/api/tests/shared/unit/application/error-manager_test.js b/api/tests/shared/unit/application/error-manager_test.js
index 78b242e1c1c..0e927843cc2 100644
--- a/api/tests/shared/unit/application/error-manager_test.js
+++ b/api/tests/shared/unit/application/error-manager_test.js
@@ -661,7 +661,7 @@ describe('Shared | Unit | Application | ErrorManager', function () {
await handle(params.request, params.h, params.error);
// then
- expect(HttpErrors.ForbiddenError).to.have.been.calledWithExactly(error.message);
+ expect(HttpErrors.ForbiddenError).to.have.been.calledWithExactly(error.message, error.code);
});
it('should instantiate ConflictError when CertificationEndedByFinalizationError', async function () {
diff --git a/certif/app/components/sessions/session-details/enrolled-candidates/index.gjs b/certif/app/components/sessions/session-details/enrolled-candidates/index.gjs
index 043deaa2f5b..e2defdd1478 100644
--- a/certif/app/components/sessions/session-details/enrolled-candidates/index.gjs
+++ b/certif/app/components/sessions/session-details/enrolled-candidates/index.gjs
@@ -234,6 +234,8 @@ export default class EnrolledCandidates extends Component {
return this._handleEntityValidationError(errorResponse);
case '400':
return this._handleMissingQueryParamError(errorResponse);
+ case '403':
+ return this._handleApiError(errorResponse);
default:
return this.intl.t(`${TRANSLATE_PREFIX}.add-modal.notifications.error-add-unknown`);
}
@@ -248,6 +250,15 @@ export default class EnrolledCandidates extends Component {
}
}
+ _handleApiError(errorResponse) {
+ const error = errorResponse?.errors?.[0];
+ if (error?.code) {
+ return this.intl.t(`common.api-error-messages.${error.code}`, {
+ ...error?.meta,
+ });
+ }
+ }
+
_handleMissingQueryParamError(errorResponse) {
const error = errorResponse?.errors?.[0];
if (error?.detail === 'CANDIDATE_BIRTHDATE_FORMAT_NOT_VALID') {
diff --git a/certif/tests/integration/components/sessions/session-details/enrolled-candidates/index-test.gjs b/certif/tests/integration/components/sessions/session-details/enrolled-candidates/index-test.gjs
index 631f8906033..cefe0405a67 100644
--- a/certif/tests/integration/components/sessions/session-details/enrolled-candidates/index-test.gjs
+++ b/certif/tests/integration/components/sessions/session-details/enrolled-candidates/index-test.gjs
@@ -48,10 +48,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it should have an accessible table description', async function (assert) {
//given
- const candidate = _buildCertificationCandidate({
- birthdate: new Date('2019-04-28'),
- subscriptions: [],
- });
+ const candidate = _buildCertificationCandidate({ subscriptions: [] });
const certificationCandidates = [store.createRecord('certification-candidate', candidate)];
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
@@ -89,7 +86,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
complementaryCertificationId,
});
const candidate = _buildCertificationCandidate({
- birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: true,
subscriptions: [coreSubscription, complementarySubscription],
});
@@ -131,9 +127,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it should display details button', async function (assert) {
// given
- const candidate = _buildCertificationCandidate({
- subscriptions: [],
- });
+ const candidate = _buildCertificationCandidate({ subscriptions: [] });
const certificationCandidates = [store.createRecord('certification-candidate', candidate)];
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
@@ -158,9 +152,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it should display details modal', async function (assert) {
// given
- const candidate = _buildCertificationCandidate({
- subscriptions: [],
- });
+ const candidate = _buildCertificationCandidate({ subscriptions: [] });
const certificationCandidates = [store.createRecord('certification-candidate', candidate)];
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
@@ -190,19 +182,9 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it should be possible to delete the candidate', async function (assert) {
// given
const certificationCandidates = [
- _buildCertificationCandidate({
- id: 1,
- firstName: 'Riri',
- lastName: 'Duck',
- subscriptions: [],
- }),
- _buildCertificationCandidate({ id: 2, firstName: 'Fifi', lastName: 'Duck', subscriptions: [] }),
- _buildCertificationCandidate({
- id: 3,
- firstName: 'Loulou',
- lastName: 'Duck',
- subscriptions: [],
- }),
+ _buildCertificationCandidate({ id: '1' }),
+ _buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage' }),
+ _buildCertificationCandidate({ id: '3', firstName: 'Jean', lastName: 'Registre' }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
@@ -221,7 +203,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
,
);
- await click(screen.getByRole('button', { name: 'Supprimer le candidat Riri Duck' }));
+ await click(screen.getByRole('button', { name: 'Supprimer le candidat Eddy Taurial' }));
// then
sinon.assert.calledOnce(certificationCandidates[0].destroyRecord);
@@ -235,21 +217,9 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it display candidates with delete button disabled', async function (assert) {
// given
const certificationCandidates = [
- _buildCertificationCandidate({
- id: 1,
- firstName: 'Riri',
- lastName: 'Duck',
- isLinked: false,
- subscriptions: [],
- }),
- _buildCertificationCandidate({ id: 2, firstName: 'Fifi', lastName: 'Duck', isLinked: true, subscriptions: [] }),
- _buildCertificationCandidate({
- id: 3,
- firstName: 'Loulou',
- lastName: 'Duck',
- isLinked: false,
- subscriptions: [],
- }),
+ _buildCertificationCandidate({ id: '1' }),
+ _buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage', isLinked: true }),
+ _buildCertificationCandidate({ id: '3', firstName: 'Jean', lastName: 'Registre' }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
@@ -266,13 +236,13 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
// then
assert
- .dom(screen.getByRole('button', { name: 'Supprimer le candidat Riri Duck' }))
+ .dom(screen.getByRole('button', { name: 'Supprimer le candidat Eddy Taurial' }))
.hasClass(DELETE_BUTTON_SELECTOR);
assert
- .dom(screen.getByRole('button', { name: 'Supprimer le candidat Fifi Duck' }))
+ .dom(screen.getByRole('button', { name: 'Supprimer le candidat Lara Pafromage' }))
.hasClass(DELETE_BUTTON_DISABLED_SELECTOR);
assert
- .dom(screen.getByRole('button', { name: 'Supprimer le candidat Loulou Duck' }))
+ .dom(screen.getByRole('button', { name: 'Supprimer le candidat Jean Registre' }))
.hasClass(DELETE_BUTTON_SELECTOR);
});
});
@@ -308,27 +278,9 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it display candidates with an edit button', async function (assert) {
// given
const certificationCandidates = [
- _buildCertificationCandidate({
- id: 1,
- firstName: 'Riri',
- lastName: 'Duck',
- isLinked: false,
- subscriptions: [],
- }),
- _buildCertificationCandidate({
- id: 2,
- firstName: 'Fifi',
- lastName: 'Duck',
- isLinked: true,
- subscriptions: [],
- }),
- _buildCertificationCandidate({
- id: 3,
- firstName: 'Loulou',
- lastName: 'Duck',
- isLinked: false,
- subscriptions: [],
- }),
+ _buildCertificationCandidate({ id: '1' }),
+ _buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage', isLinked: true }),
+ _buildCertificationCandidate({ id: '3', firstName: 'Jean', lastName: 'Registre' }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
@@ -346,12 +298,14 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
// then
// then
- assert.dom(screen.getByRole('button', { name: 'Editer le candidat Riri Duck' })).hasClass(EDIT_BUTTON_SELECTOR);
assert
- .dom(screen.getByRole('button', { name: 'Editer le candidat Fifi Duck' }))
+ .dom(screen.getByRole('button', { name: 'Editer le candidat Eddy Taurial' }))
+ .hasClass(EDIT_BUTTON_SELECTOR);
+ assert
+ .dom(screen.getByRole('button', { name: 'Editer le candidat Lara Pafromage' }))
.hasClass(EDIT_BUTTON_DISABLED_SELECTOR);
assert
- .dom(screen.getByRole('button', { name: 'Editer le candidat Loulou Duck' }))
+ .dom(screen.getByRole('button', { name: 'Editer le candidat Jean Registre' }))
.hasClass(EDIT_BUTTON_SELECTOR);
assert.strictEqual(
screen.getAllByText("Ce candidat a déjà rejoint la session. Vous ne pouvez pas l'éditer.").length,
@@ -363,7 +317,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('should display candidate needs accessibility adjusted certification', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
- birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: true,
});
@@ -391,7 +344,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('should display candidate doesnt need accessibility adjusted certification', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
- birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: false,
});
@@ -420,7 +372,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('should not display accessibility adjusted certification needed information', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
- birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: true,
});
@@ -450,20 +401,8 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it does not display candidates with an edit button', async function (assert) {
// given
const certificationCandidates = [
- _buildCertificationCandidate({
- id: 1,
- firstName: 'Riri',
- lastName: 'Duck',
- isLinked: false,
- subscriptions: [],
- }),
- _buildCertificationCandidate({
- id: 2,
- firstName: 'Fifi',
- lastName: 'Duck',
- isLinked: true,
- subscriptions: [],
- }),
+ _buildCertificationCandidate({ id: '1' }),
+ _buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage', isLinked: true }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
@@ -479,8 +418,8 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
);
// then
- assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Riri Duck' })).doesNotExist();
- assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Fifi Duck' })).doesNotExist();
+ assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Eddy Taurial' })).doesNotExist();
+ assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Lara Pafromage' })).doesNotExist();
});
});
});
@@ -498,7 +437,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
complementaryCertificationId: cleaCertificationId,
});
const candidate = _buildCertificationCandidate({
- birthdate: new Date('2019-04-28'),
subscriptions: [coreSubscription, complementarySubscription],
});
const complementaryCertifications = [
@@ -728,18 +666,18 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
function _buildCertificationCandidate({
id = '12345',
- firstName = 'Bob',
- lastName = 'Leponge',
- birthdate = new Date(),
- birthCity = 'Marseille',
- birthProvinceCode = '',
- birthCountry = '',
- email = 'bob.leponge@la.mer',
- resultRecipientEmail = 'recipient@college.fr',
+ firstName = 'Eddy',
+ lastName = 'Taurial',
+ birthdate = '1990-03-22',
+ birthCity = 'Sainte-Anne',
+ birthProvinceCode = '01',
+ birthCountry = 'France',
+ email = 'eddy.taurial@example.com',
+ resultRecipientEmail = 'pat.atrak@example.com',
externalId = 'an external id',
extraTimePercentage = 0.3,
isLinked = false,
- billingMode = null,
+ billingMode = 'FREE',
prepaymentCode = null,
accessibilityAdjustmentNeeded = false,
subscriptions = [],
diff --git a/certif/translations/en.json b/certif/translations/en.json
index 39e5cbdfe64..9d479fd043c 100644
--- a/certif/translations/en.json
+++ b/certif/translations/en.json
@@ -21,6 +21,7 @@
"validate": "Validate"
},
"api-error-messages": {
+ "CANDIDATE_NOT_ALLOWED_FOR_FINALIZED_SESSION": "This session has already been finalised and no new candidates may be added.",
"CANDIDATE_NOT_FOUND": "An error has occurred, the candidate {firstName} {lastName} hasn't been found.",
"INVALID_DOCUMENT": "This version of the document is unknown.
Please download and import the candidate list template again",
"SESSION_ALREADY_FINALIZED": "Cannot finalize session more than once.",
@@ -1029,4 +1030,4 @@
"page-title": "Terms and conditions"
}
}
-}
\ No newline at end of file
+}
diff --git a/certif/translations/fr.json b/certif/translations/fr.json
index ebece47902c..3db223d2430 100644
--- a/certif/translations/fr.json
+++ b/certif/translations/fr.json
@@ -21,6 +21,7 @@
"validate": "Valider"
},
"api-error-messages": {
+ "CANDIDATE_NOT_ALLOWED_FOR_FINALIZED_SESSION": "Cette session a déjà été finalisée, l'ajout de candidat n'est pas autorisé.",
"CANDIDATE_NOT_FOUND": "Une erreur est survenue, le candidat {firstName} {lastName} n'a pas été trouvé.",
"INVALID_DOCUMENT": "La version du document est inconnue.
Veuillez télécharger à nouveau le modèle de liste des candidats et l'importer à nouveau.",
"SESSION_ALREADY_FINALIZED": "La session a déjà été finalisée.",
@@ -1029,4 +1030,4 @@
"page-title": "Conditions générales"
}
}
-}
\ No newline at end of file
+}