diff --git a/api/src/prescription/organization-place/domain/usecases/get-data-organizations-places-statistics.js b/api/src/prescription/organization-place/domain/usecases/get-data-organizations-places-statistics.js index f843373577e..4c9dce1f832 100644 --- a/api/src/prescription/organization-place/domain/usecases/get-data-organizations-places-statistics.js +++ b/api/src/prescription/organization-place/domain/usecases/get-data-organizations-places-statistics.js @@ -7,7 +7,7 @@ const getDataOrganizationsPlacesStatistics = withTransaction(async function ({ organizationPlacesLotRepository, organizationLearnerRepository, }) { - const organizationWithPlaces = await organizationRepository.getOrganizationsWithPlaces(); + const organizationWithPlaces = await organizationRepository.getOrganizationsWithPlacesManagementFeatureEnabled(); const organizationWithPlacesIds = organizationWithPlaces.map((organization) => organization.id); diff --git a/api/src/shared/infrastructure/repositories/organization-repository.js b/api/src/shared/infrastructure/repositories/organization-repository.js index ca6de3704d4..87791bba2f4 100644 --- a/api/src/shared/infrastructure/repositories/organization-repository.js +++ b/api/src/shared/infrastructure/repositories/organization-repository.js @@ -3,6 +3,7 @@ import _ from 'lodash'; import { knex } from '../../../../db/knex-database-connection.js'; import { Organization } from '../../../organizational-entities/domain/models/Organization.js'; import { Tag } from '../../../organizational-entities/domain/models/Tag.js'; +import { ORGANIZATION_FEATURE } from '../../../shared/domain/constants.js'; import { DomainTransaction } from '../../domain/DomainTransaction.js'; import { NotFoundError } from '../../domain/errors.js'; import { fetchPage } from '../utils/knex-utils.js'; @@ -184,13 +185,26 @@ const findPaginatedFilteredByTargetProfile = async function ({ targetProfileId, return { models: organizations, pagination }; }; -const getOrganizationsWithPlaces = async function () { +const getOrganizationsWithPlacesManagementFeatureEnabled = async function () { const knexConn = DomainTransaction.getConnection(); + const placesManagementFeature = await knexConn('features') + .select('id') + .where('key', ORGANIZATION_FEATURE.PLACES_MANAGEMENT.key) + .first(); + + if (!placesManagementFeature) { + return []; + } + const organizations = await knexConn('organizations') .select('organizations.id', 'name', 'type') - .innerJoin('organization-places', 'organizations.id', 'organization-places.organizationId') - .whereNull('archivedAt') - .distinct(); + .join('organization-features', function () { + this.on('organization-features.organizationId', 'organizations.id').andOn( + 'organization-features.featureId', + placesManagementFeature.id, + ); + }) + .whereNull('archivedAt'); return organizations.map((organization) => _toDomain(organization)); }; @@ -204,6 +218,6 @@ export { findScoOrganizationsByUai, get, getIdByCertificationCenterId, - getOrganizationsWithPlaces, + getOrganizationsWithPlacesManagementFeatureEnabled, update, }; diff --git a/api/tests/integration/infrastructure/repositories/organization-repository_test.js b/api/tests/integration/infrastructure/repositories/organization-repository_test.js index 6c1d8ff149f..29f1a2fcff3 100644 --- a/api/tests/integration/infrastructure/repositories/organization-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/organization-repository_test.js @@ -1,6 +1,7 @@ import _ from 'lodash'; import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../src/identity-access-management/domain/constants/identity-providers.js'; +import { ORGANIZATION_FEATURE } from '../../../../src/shared/domain/constants.js'; import { NotFoundError } from '../../../../src/shared/domain/errors.js'; import { Organization } from '../../../../src/shared/domain/models/index.js'; import * as organizationRepository from '../../../../src/shared/infrastructure/repositories/organization-repository.js'; @@ -1000,173 +1001,173 @@ describe('Integration | Repository | Organization', function () { }); }); - describe('#getOrganizationsWithPlaces', function () { - it('should only return organizations not archived', async function () { - // given - const superAdminUserId = databaseBuilder.factory.buildUser().id; - - const firstOrganization = databaseBuilder.factory.buildOrganization({ - type: 'SCO', - name: 'Organization of the dark side', - archivedAt: null, - isArchived: false, - }); + describe('#getOrganizationsWithPlacesManagementFeatureEnabled', function () { + describe('When organization have places management feature enabled', function () { + let firstOrganization; - databaseBuilder.factory.buildOrganizationPlace({ - count: 3, - organizationId: firstOrganization.id, - activationDate: new Date(), - expirationDate: new Date(), - createdBy: superAdminUserId, - createdAt: new Date(), - deletedAt: null, - deletedBy: null, - }); - - const secondOrganization = databaseBuilder.factory.buildOrganization({ - type: 'SCO', - name: 'Organization of the BRIGHT side', - archivedAt: new Date(), - isArchived: true, - }); + beforeEach(async function () { + firstOrganization = databaseBuilder.factory.buildOrganization({ + type: 'SCO', + name: 'Organization of the dark side', + archivedAt: null, + isArchived: false, + }); + const placesManagementFeatureId = databaseBuilder.factory.buildFeature({ + key: ORGANIZATION_FEATURE.PLACES_MANAGEMENT.key, + }).id; + databaseBuilder.factory.buildOrganizationFeature({ + organizationId: firstOrganization.id, + featureId: placesManagementFeatureId, + }); - databaseBuilder.factory.buildOrganizationPlace({ - count: 3, - organizationId: secondOrganization.id, - activationDate: new Date(), - expirationDate: new Date(), - createdBy: superAdminUserId, - createdAt: new Date(), - deletedAt: null, - deletedBy: null, + await databaseBuilder.commit(); }); - await databaseBuilder.commit(); + it('should only return organizations not archived', async function () { + // given + const superAdminUserId = databaseBuilder.factory.buildUser().id; - // when - const organizationsWithPlaces = await organizationRepository.getOrganizationsWithPlaces(); + databaseBuilder.factory.buildOrganizationPlace({ + count: 3, + organizationId: firstOrganization.id, + activationDate: new Date(), + expirationDate: new Date(), + createdBy: superAdminUserId, + createdAt: new Date(), + deletedAt: null, + deletedBy: null, + }); - // then - expect(organizationsWithPlaces.length).to.equal(1); - expect(organizationsWithPlaces[0]).to.be.instanceOf(Organization); - expect(organizationsWithPlaces[0].id).to.equal(firstOrganization.id); - expect(organizationsWithPlaces[0].name).to.equal(firstOrganization.name); - expect(organizationsWithPlaces[0].type).to.equal(firstOrganization.type); - }); + const secondOrganization = databaseBuilder.factory.buildOrganization({ + type: 'SCO', + name: 'Organization of the BRIGHT side', + archivedAt: new Date(), + isArchived: true, + }); - it('should only return organizations with places', async function () { - // given - const superAdminUserId = databaseBuilder.factory.buildUser().id; + databaseBuilder.factory.buildOrganizationPlace({ + count: 3, + organizationId: secondOrganization.id, + activationDate: new Date(), + expirationDate: new Date(), + createdBy: superAdminUserId, + createdAt: new Date(), + deletedAt: null, + deletedBy: null, + }); - const firstOrganization = databaseBuilder.factory.buildOrganization({ - type: 'SCO', - name: 'Organization of the dark side', - archivedAt: null, - isArchived: false, - }); + await databaseBuilder.commit(); - databaseBuilder.factory.buildOrganizationPlace({ - count: 3, - organizationId: firstOrganization.id, - activationDate: new Date(), - expirationDate: new Date(), - createdBy: superAdminUserId, - createdAt: new Date(), - deletedAt: null, - deletedBy: null, - }); + // when + const organizationsWithPlaces = + await organizationRepository.getOrganizationsWithPlacesManagementFeatureEnabled(); - databaseBuilder.factory.buildOrganization({ - type: 'SCO', - name: 'Organization of the rainbow side', - archivedAt: null, - isArchived: false, + // then + expect(organizationsWithPlaces.length).to.equal(1); + expect(organizationsWithPlaces[0]).to.be.instanceOf(Organization); + expect(organizationsWithPlaces[0].id).to.equal(firstOrganization.id); + expect(organizationsWithPlaces[0].name).to.equal(firstOrganization.name); + expect(organizationsWithPlaces[0].type).to.equal(firstOrganization.type); }); - await databaseBuilder.commit(); + it('should return only once an organization with many placesLots', async function () { + // given + const superAdminUserId = databaseBuilder.factory.buildUser().id; - // when - const organizationsWithPlaces = await organizationRepository.getOrganizationsWithPlaces(); + databaseBuilder.factory.buildOrganizationPlace({ + count: 3, + organizationId: firstOrganization.id, + activationDate: new Date(), + expirationDate: new Date(), + createdBy: superAdminUserId, + createdAt: new Date(), + deletedAt: null, + deletedBy: null, + }); - // then - expect(organizationsWithPlaces.length).to.equal(1); - expect(organizationsWithPlaces[0]).to.be.instanceOf(Organization); - expect(organizationsWithPlaces[0].id).to.equal(firstOrganization.id); - expect(organizationsWithPlaces[0].name).to.equal(firstOrganization.name); - expect(organizationsWithPlaces[0].type).to.equal(firstOrganization.type); - }); + databaseBuilder.factory.buildOrganizationPlace({ + count: 25, + organizationId: firstOrganization.id, + activationDate: new Date(), + expirationDate: new Date(), + createdBy: superAdminUserId, + createdAt: new Date(), + deletedAt: null, + deletedBy: null, + }); - it('should return only once an organization with many placesLots', async function () { - // given - const superAdminUserId = databaseBuilder.factory.buildUser().id; + await databaseBuilder.commit(); - const firstOrganization = databaseBuilder.factory.buildOrganization({ - type: 'SCO', - name: 'Organization of the dark side', - archivedAt: null, - isArchived: false, - }); + // when + const organizationsWithPlaces = + await organizationRepository.getOrganizationsWithPlacesManagementFeatureEnabled(); - databaseBuilder.factory.buildOrganizationPlace({ - count: 3, - organizationId: firstOrganization.id, - activationDate: new Date(), - expirationDate: new Date(), - createdBy: superAdminUserId, - createdAt: new Date(), - deletedAt: null, - deletedBy: null, + // then + expect(organizationsWithPlaces.length).to.equal(1); }); - databaseBuilder.factory.buildOrganizationPlace({ - count: 25, - organizationId: firstOrganization.id, - activationDate: new Date(), - expirationDate: new Date(), - createdBy: superAdminUserId, - createdAt: new Date(), - deletedAt: null, - deletedBy: null, - }); + it('should return organization instead if they have unlimited places', async function () { + // given + const superAdminUserId = databaseBuilder.factory.buildUser().id; - await databaseBuilder.commit(); + const organizationId = databaseBuilder.factory.buildOrganization({ + type: 'SCO', + name: 'Organization du sud de la France avec le plus beau stade de France', + archivedAt: null, + isArchived: false, + }).id; - // when - const organizationsWithPlaces = await organizationRepository.getOrganizationsWithPlaces(); + databaseBuilder.factory.buildOrganizationPlace({ + count: null, + organizationId, + activationDate: new Date('2024-01-01'), + expirationDate: new Date('2025-12-31'), + createdBy: superAdminUserId, + createdAt: new Date(), + deletedAt: null, + deletedBy: null, + }); - // then - expect(organizationsWithPlaces.length).to.equal(1); - }); + await databaseBuilder.commit(); - it('should return organization instead if they have unlimited places', async function () { - // given - const superAdminUserId = databaseBuilder.factory.buildUser().id; + // when + const organizationsWithPlaces = + await organizationRepository.getOrganizationsWithPlacesManagementFeatureEnabled(); - const organizationId = databaseBuilder.factory.buildOrganization({ - type: 'SCO', - name: 'Organization du sud de la France avec le plus beau stade de France', - archivedAt: null, - isArchived: false, - }).id; - - databaseBuilder.factory.buildOrganizationPlace({ - count: null, - organizationId, - activationDate: new Date('2024-01-01'), - expirationDate: new Date('2025-12-31'), - createdBy: superAdminUserId, - createdAt: new Date(), - deletedAt: null, - deletedBy: null, + // then + expect(organizationsWithPlaces.length).to.equal(1); }); + }); - await databaseBuilder.commit(); + describe('When organization have places management feature not enabled', function () { + it('should not return organization', async function () { + // given + const userId = databaseBuilder.factory.buildUser().id; + const organizationId = databaseBuilder.factory.buildOrganization({ + type: 'PRO', + name: 'Organization without places feature', + isArchived: false, + }).id; + databaseBuilder.factory.buildOrganizationPlace({ + count: 3, + organizationId, + activationDate: new Date(), + expirationDate: new Date(), + createdBy: userId, + createdAt: new Date(), + deletedAt: null, + deletedBy: null, + }); - // when - const organizationsWithPlaces = await organizationRepository.getOrganizationsWithPlaces(); + await databaseBuilder.commit(); - // then - expect(organizationsWithPlaces.length).to.equal(1); + // when + const organizationsWithPlaces = + await organizationRepository.getOrganizationsWithPlacesManagementFeatureEnabled(); + + // then + expect(organizationsWithPlaces).to.have.lengthOf(0); + }); }); }); }); diff --git a/api/tests/prescription/organization-place/integration/domain/usecases/get-data-organizations-places-statistics_test.js b/api/tests/prescription/organization-place/integration/domain/usecases/get-data-organizations-places-statistics_test.js index 0474e6709b0..e09f2e680d9 100644 --- a/api/tests/prescription/organization-place/integration/domain/usecases/get-data-organizations-places-statistics_test.js +++ b/api/tests/prescription/organization-place/integration/domain/usecases/get-data-organizations-places-statistics_test.js @@ -1,6 +1,7 @@ import * as organizationLearnerRepository from '../../../../../../lib/infrastructure/repositories/organization-learner-repository.js'; import { getDataOrganizationsPlacesStatistics } from '../../../../../../src/prescription/organization-place/domain/usecases/get-data-organizations-places-statistics.js'; import * as organizationPlacesLotRepository from '../../../../../../src/prescription/organization-place/infrastructure/repositories/organization-places-lot-repository.js'; +import { ORGANIZATION_FEATURE } from '../../../../../../src/shared/domain/constants.js'; import * as organizationRepository from '../../../../../../src/shared/infrastructure/repositories/organization-repository.js'; import { databaseBuilder, expect, sinon } from '../../../../../test-helper.js'; @@ -23,6 +24,14 @@ describe('Integration | UseCases | get-data-organizations-places-statistics', fu name: 'Mon collège', type: 'SCO', }); + const placesManagementFeatureId = databaseBuilder.factory.buildFeature({ + key: ORGANIZATION_FEATURE.PLACES_MANAGEMENT.key, + }).id; + databaseBuilder.factory.buildOrganizationFeature({ + organizationId: firstOrganization.id, + featureId: placesManagementFeatureId, + }); + const organizationLearnerId = databaseBuilder.factory.buildOrganizationLearner({ organizationId: firstOrganization.id, }).id; @@ -41,6 +50,12 @@ describe('Integration | UseCases | get-data-organizations-places-statistics', fu activationDate: new Date('2021-04-01'), expirationDate: new Date('2021-05-15'), }); + + databaseBuilder.factory.buildOrganizationFeature({ + organizationId: secondOrganization.id, + featureId: placesManagementFeatureId, + }); + await databaseBuilder.commit(); // when