Skip to content

Commit

Permalink
[TECH] Migrer la route reconciliation SCO dans son bounded Context (P…
Browse files Browse the repository at this point in the history
  • Loading branch information
pix-service-auto-merge authored Nov 21, 2024
2 parents da12267 + e03541d commit 7956c7c
Show file tree
Hide file tree
Showing 19 changed files with 515 additions and 480 deletions.
31 changes: 0 additions & 31 deletions api/lib/application/sco-organization-learners/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,6 @@ const inaPattern = new RegExp('^[0-9]{10}[a-zA-Z]{1}$');

const register = async function (server) {
server.route([
{
method: 'POST',
path: '/api/sco-organization-learners/association',
config: {
handler: scoOrganizationLearnerController.reconcileScoOrganizationLearnerManually,
validate: {
options: {
allowUnknown: false,
},
payload: Joi.object({
data: {
attributes: {
'first-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
'last-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
birthdate: Joi.date().format('YYYY-MM-DD').required(),
'campaign-code': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
},
type: 'sco-organization-learners',
},
}),
failAction: (request, h) => {
return sendJsonApiError(new UnprocessableEntityError('Un des champs saisis n’est pas valide.'), h);
},
},
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
'- Elle associe des données saisies par l’utilisateur à l’inscription de l’élève dans cette organisation',
],
tags: ['api', 'sco-organization-learners'],
},
},
{
method: 'PUT',
path: '/api/sco-organization-learners/possibilities',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,11 @@
import dayjs from 'dayjs';

import * as scoOrganizationLearnerSerializer from '../../../src/prescription/learner-management/infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';
import * as requestResponseUtils from '../../../src/shared/infrastructure/utils/request-response-utils.js';
import { usecases } from '../../domain/usecases/index.js';
import { DomainTransaction } from '../../infrastructure/DomainTransaction.js';
import * as scoOrganizationLearnerSerializer from '../../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';
import * as studentInformationForAccountRecoverySerializer from '../../infrastructure/serializers/jsonapi/student-information-for-account-recovery-serializer.js';

const reconcileScoOrganizationLearnerManually = async function (
request,
h,
dependencies = { scoOrganizationLearnerSerializer },
) {
const authenticatedUserId = request.auth.credentials.userId;
const payload = request.payload.data.attributes;
const campaignCode = payload['campaign-code'];
const withReconciliation = request.query.withReconciliation === 'true';

const reconciliationInfo = {
id: authenticatedUserId,
firstName: payload['first-name'],
lastName: payload['last-name'],
birthdate: payload['birthdate'],
};

const organizationLearner = await usecases.reconcileScoOrganizationLearnerManually({
campaignCode,
reconciliationInfo,
withReconciliation,
});

let response;
if (withReconciliation) {
const serializedData = dependencies.scoOrganizationLearnerSerializer.serializeIdentity(organizationLearner);
response = h.response(serializedData).code(200);
} else {
response = h.response().code(204);
}
return response;
};

const generateUsername = async function (request, h, dependencies = { scoOrganizationLearnerSerializer }) {
const payload = request.payload.data.attributes;
const { 'campaign-code': campaignCode } = payload;
Expand Down Expand Up @@ -191,7 +158,6 @@ const batchGenerateOrganizationLearnersUsernameWithTemporaryPassword = async fun
};

const scoOrganizationLearnerController = {
reconcileScoOrganizationLearnerManually,
generateUsername,
createAndReconcileUserToOrganizationLearner,
createUserAndReconcileToOrganizationLearnerFromExternalUser,
Expand Down
2 changes: 1 addition & 1 deletion api/lib/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import * as campaignProfileRepository from '../../../src/prescription/campaign-p
import { participationCompletedJobRepository } from '../../../src/prescription/campaign-participation/infrastructure/repositories/jobs/participation-completed-job-repository.js';
import * as poleEmploiSendingRepository from '../../../src/prescription/campaign-participation/infrastructure/repositories/pole-emploi-sending-repository.js';
import * as prescriptionOrganizationLearnerRepository from '../../../src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js';
import * as studentRepository from '../../../src/prescription/learner-management/infrastructure/repositories/student-repository.js';
import * as organizationLearnerActivityRepository from '../../../src/prescription/organization-learner/infrastructure/repositories/organization-learner-activity-repository.js';
import * as registrationOrganizationLearnerRepository from '../../../src/prescription/organization-learner/infrastructure/repositories/registration-organization-learner-repository.js';
import * as targetProfileSummaryForAdminRepository from '../../../src/prescription/target-profile/infrastructure/repositories/target-profile-summary-for-admin-repository.js';
Expand Down Expand Up @@ -144,7 +145,6 @@ import * as organizationLearnerRepository from '../../infrastructure/repositorie
import * as organizationMemberIdentityRepository from '../../infrastructure/repositories/organization-member-identity-repository.js';
import * as organizationTagRepository from '../../infrastructure/repositories/organization-tag-repository.js';
import { participantResultsSharedRepository } from '../../infrastructure/repositories/participant-results-shared-repository.js';
import * as studentRepository from '../../infrastructure/repositories/student-repository.js';
import * as targetProfileRepository from '../../infrastructure/repositories/target-profile-repository.js';
import * as targetProfileShareRepository from '../../infrastructure/repositories/target-profile-share-repository.js';
import * as targetProfileTrainingRepository from '../../infrastructure/repositories/target-profile-training-repository.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,6 @@ import { OrganizationLearner } from '../../../src/shared/domain/models/Organizat
import { ParticipantRepartition } from '../../../src/shared/domain/models/ParticipantRepartition.js';
import { fetchPage } from '../../../src/shared/infrastructure/utils/knex-utils.js';
import { DomainTransaction } from '../DomainTransaction.js';
import * as studentRepository from './student-repository.js';

function _shouldStudentToImportBeReconciled(
allOrganizationLearnersInSameOrganization,
organizationLearner,
studentToImport,
) {
const organizationLearnerWithSameUserId = allOrganizationLearnersInSameOrganization.find(
(organizationLearnerInSameOrganization) => {
return organizationLearnerInSameOrganization.userId === organizationLearner.account.userId;
},
);
const isOrganizationLearnerReconciled = organizationLearnerWithSameUserId != null;
const organizationLearnerHasSameUserIdAndNationalStudentId =
organizationLearnerWithSameUserId?.nationalStudentId === organizationLearner.nationalStudentId;

if (isOrganizationLearnerReconciled && !organizationLearnerHasSameUserIdAndNationalStudentId) {
return false;
}

const isFromSameOrganization = studentToImport.organizationId === organizationLearner.account.organizationId;
const isFromDifferentOrganizationWithSameBirthday =
!isFromSameOrganization && studentToImport.birthdate === organizationLearner.account.birthdate;
return isFromSameOrganization || isFromDifferentOrganizationWithSameBirthday;
}

const findByIds = async function ({ ids }) {
const rawOrganizationLearners = await knex
Expand Down Expand Up @@ -89,40 +64,6 @@ const findByUserId = async function ({ userId }) {
return rawOrganizationLearners.map((rawOrganizationLearner) => new OrganizationLearner(rawOrganizationLearner));
};

const _reconcileOrganizationLearners = async function (studentsToImport, allOrganizationLearnersInSameOrganization) {
const nationalStudentIdsFromFile = studentsToImport
.map((organizationLearnerData) => organizationLearnerData.nationalStudentId)
.filter(Boolean);
const organizationLearnersWithSameNationalStudentIdsAsImported =
await studentRepository.findReconciledStudentsByNationalStudentId(nationalStudentIdsFromFile);

organizationLearnersWithSameNationalStudentIdsAsImported.forEach((organizationLearner) => {
const alreadyReconciledStudentToImport = studentsToImport.find(
(studentToImport) => studentToImport.userId === organizationLearner.account.userId,
);

if (alreadyReconciledStudentToImport) {
alreadyReconciledStudentToImport.userId = null;
return;
}

const studentToImport = studentsToImport.find(
(studentToImport) => studentToImport.nationalStudentId === organizationLearner.nationalStudentId,
);

if (
_shouldStudentToImportBeReconciled(
allOrganizationLearnersInSameOrganization,
organizationLearner,
studentToImport,
)
) {
studentToImport.userId = organizationLearner.account.userId;
}
});
return studentsToImport;
};

const findByOrganizationIdAndBirthdate = async function ({ organizationId, birthdate }) {
const rawOrganizationLearners = await knex
.select('*')
Expand Down Expand Up @@ -347,7 +288,6 @@ const findAllLearnerWithAtLeastOneParticipationByOrganizationIds = async functio
};

export {
_reconcileOrganizationLearners,
countByOrganizationsWhichNeedToComputeCertificability,
dissociateAllStudentsByUserId,
findAllLearnerWithAtLeastOneParticipationByOrganizationId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as scoOrganizationLearnerSerializer from '../../../../lib/infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';
import { DomainTransaction } from '../../../shared/domain/DomainTransaction.js';
import { ApplicationTransaction } from '../../shared/infrastructure/ApplicationTransaction.js';
import { usecases } from '../domain/usecases/index.js';
import * as scoOrganizationLearnerSerializer from '../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';

const deleteOrganizationLearners = async function (request, h) {
const authenticatedUserId = request.auth.credentials.userId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../../../../src/shared/infrastructure/monitoring-tools.js';
import { usecases } from '../domain/usecases/index.js';
import { OrganizationLearnerParser } from '../infrastructure/serializers/csv/organization-learner-parser.js';
import * as scoOrganizationLearnerSerializer from '../infrastructure/serializers/jsonapi/sco-organization-learner-serializer.js';

const INVALID_FILE_EXTENSION_ERROR = 'INVALID_FILE_EXTENSION';

Expand Down Expand Up @@ -66,8 +67,42 @@ const importOrganizationLearnersFromSIECLE = async function (
return h.response(null).code(204);
};

const reconcileScoOrganizationLearnerManually = async function (
request,
h,
dependencies = { scoOrganizationLearnerSerializer },
) {
const authenticatedUserId = request.auth.credentials.userId;
const payload = request.payload.data.attributes;
const campaignCode = payload['campaign-code'];
const withReconciliation = request.query.withReconciliation === 'true';

const reconciliationInfo = {
id: authenticatedUserId,
firstName: payload['first-name'],
lastName: payload['last-name'],
birthdate: payload['birthdate'],
};

const organizationLearner = await usecases.reconcileScoOrganizationLearnerManually({
campaignCode,
reconciliationInfo,
withReconciliation,
});

let response;
if (withReconciliation) {
const serializedData = dependencies.scoOrganizationLearnerSerializer.serializeIdentity(organizationLearner);
response = h.response(serializedData).code(200);
} else {
response = h.response().code(204);
}
return response;
};

const scoOrganizationManagementController = {
importOrganizationLearnersFromSIECLE,
reconcileScoOrganizationLearnerManually,
};

export { scoOrganizationManagementController };
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import JoiDate from '@joi/date';
import BaseJoi from 'joi';
const Joi = BaseJoi.extend(JoiDate);

import { sendJsonApiError } from '../../../shared/application/http-errors.js';
import { sendJsonApiError, UnprocessableEntityError } from '../../../shared/application/http-errors.js';
import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js';
import { identifiersType } from '../../../shared/domain/types/identifiers-type.js';
import { usecases } from '../domain/usecases/index.js';
Expand All @@ -12,6 +12,37 @@ const TWENTY_MEGABYTES = 1048576 * 20;

const register = async function (server) {
server.route([
{
method: 'POST',
path: '/api/sco-organization-learners/association',
config: {
handler: scoOrganizationManagementController.reconcileScoOrganizationLearnerManually,
validate: {
options: {
allowUnknown: false,
},
payload: Joi.object({
data: {
attributes: {
'first-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
'last-name': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
birthdate: Joi.date().format('YYYY-MM-DD').required(),
'campaign-code': Joi.string().empty(Joi.string().regex(/^\s*$/)).required(),
},
type: 'sco-organization-learners',
},
}),
failAction: (request, h) => {
return sendJsonApiError(new UnprocessableEntityError('Un des champs saisis n’est pas valide.'), h);
},
},
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
'- Elle associe des données saisies par l’utilisateur à l’inscription de l’élève dans cette organisation',
],
tags: ['api', 'sco-organization-learners'],
},
},
{
method: 'POST',
path: '/api/organizations/{id}/sco-organization-learners/import-siecle',
Expand Down
Loading

0 comments on commit 7956c7c

Please sign in to comment.