Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ export const prévisualiserAttestation = _avecIdentifiant('/previsualiser-attest
// TODO: à supprimer pour utiliser directement Routes.Document.télécharger dans le front
// une fois qu'on aura migré la page Projet
export const téléchargerAttestation = _avecIdentifiant('/telecharger-attestation');

export const exporterFournisseur = '/candidatures/export-fournisseurs';
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { Option } from '@potentiel-libraries/monads';
import { DateTime, Email } from '@potentiel-domain/common';
import { findProjection, listProjection } from '@potentiel-infrastructure/pg-projection-read';
import { Document, IdentifiantProjet, Candidature, Lauréat } from '@potentiel-domain/projet';
import { ProjetAdapter, DocumentAdapter } from '@potentiel-infrastructure/domain-adapters';
import {
ProjetAdapter,
DocumentAdapter,
getProjetUtilisateurScopeAdapter,
} from '@potentiel-infrastructure/domain-adapters';
import { Période } from '@potentiel-domain/periode';

export const dgecEmail = '[email protected]';
Expand Down Expand Up @@ -38,6 +42,7 @@ Candidature.registerCandidatureQueries({
list: listProjection,
récupérerProjetsEligiblesPreuveRecanditure:
ProjetAdapter.récupérerProjetsEligiblesPreuveRecanditureAdapter,
getScopeProjetUtilisateur: getProjetUtilisateurScopeAdapter,
});

Document.registerDocumentProjetCommand({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { mediator } from 'mediateur';

import { Candidature, IdentifiantProjet } from '@potentiel-domain/projet';
import { ExportCSV } from '@potentiel-libraries/csv';

import { apiAction } from '@/utils/apiAction';
import { withUtilisateur } from '@/utils/withUtilisateur';

type CandidatureFournisseurCSV = {
identifiantProjet: IdentifiantProjet.RawType;
appelOffre: IdentifiantProjet.ValueType['appelOffre'];
periode: IdentifiantProjet.ValueType['période'];
region: Candidature.Localité.ValueType['région'];
societeMere: string;
} & Record<string, string>;

export const GET = async (_: Request) =>
apiAction(async () =>
withUtilisateur(async (utilisateur) => {
const fournisseursÀLaCandidature =
await mediator.send<Candidature.ListerFournisseursÀLaCandidatureQuery>({
type: 'Candidature.Query.ListerFournisseursÀLaCandidature',
data: {
utilisateur: utilisateur.identifiantUtilisateur.email,
},
});

const fournisseurCandidatureFields = Candidature.fournisseursCandidatureDétailKeys.map(
(key) => ({ label: key.replace(/\n/g, ''), value: key }),
);

const csv = await ExportCSV.toCSV<CandidatureFournisseurCSV>({
fields: [
{ label: 'Identifiant projet', value: 'identifiantProjet' },
{ label: "Appel d'offre", value: 'appelOffre' },
{ label: 'Période', value: 'periode' },
{ label: 'Région', value: 'region' },
{ label: 'Société mère', value: 'societeMere' },
...fournisseurCandidatureFields,
],
data: fournisseursÀLaCandidature.items.map((fournisseur) => ({
identifiantProjet: fournisseur.identifiantProjet.formatter(),
appelOffre: fournisseur.identifiantProjet.appelOffre,
periode: fournisseur.identifiantProjet.période,
region: fournisseur.région,
societeMere: fournisseur.sociétéMère,
...Object.entries(fournisseur.détail).reduce(
(acc: Record<string, string>, [key, value]) => {
acc[key] = value;
return acc;
},
{},
),
})),
});

const fileName = `export_candidature_fournisseurs.csv`;

return new Response(csv, {
headers: {
'content-type': 'text/csv',
'content-disposition': `attachment; filename=${fileName}`,
},
});
}),
);
39 changes: 32 additions & 7 deletions packages/applications/ssr/src/app/export/export.page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FC } from 'react';
import Notice from '@codegouvfr/react-dsfr/Notice';

import { Routes } from '@potentiel-applications/routes';

Expand All @@ -7,17 +8,41 @@ import { Heading1 } from '@/components/atoms/headings';
import { LinkAction } from '@/components/atoms/LinkAction';

export type ExportPageProps = {
actions: Array<'exporter-raccordement'>;
actions: Array<'exporter-raccordement' | 'exporter-fournisseur'>;
};

export const ExportPage: FC<ExportPageProps> = ({ actions }) => (
<PageTemplate banner={<Heading1>Exporter des données projets</Heading1>} feature={'export'}>
<div className="mb-4">
Cette page permet d'accéder à des liens pour exporter des données projets
</div>
<div className="flex flex-col gap-8">
{actions.includes('exporter-raccordement') && (
<LinkAction
label="Exporter les données raccordement des projets (fichier csv)"
href={Routes.Raccordement.exporter}
/>
)}

{actions.includes('exporter-raccordement') && (
<LinkAction label="Exporter les données raccordement" href={Routes.Raccordement.exporter} />
)}
{actions.includes('exporter-fournisseur') && (
<div>
<LinkAction
label="Exporter les données fournisseur à la candidature des projets (fichier csv)"
href={Routes.Candidature.exporterFournisseur}
/>
<Notice
severity="info"
title=""
className="mt-2 mb-4"
description={
<>
<span>
Attention, cet export concerne uniquement les données fournisseurs importées à la
candidature des projets. Il ne tient pas compte des éventuelles modifications
apportées au cours de la vie des projets.
</span>
</>
}
/>
</div>
)}
</div>
</PageTemplate>
);
10 changes: 8 additions & 2 deletions packages/applications/ssr/src/app/export/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ export default async function Page() {
type MapToAction = (utilisateur: PotentielUtilisateur) => ExportPageProps['actions'];

const mapToAction: MapToAction = (utilisateur) => {
const actions: ExportPageProps['actions'] = [];

if (utilisateur.rôle.aLaPermission('raccordement.listerDossierRaccordement')) {
return ['exporter-raccordement'];
actions.push('exporter-raccordement');
}

if (utilisateur.rôle.aLaPermission('candidature.exporterFournisseurs')) {
actions.push('exporter-fournisseur');
}

return [];
return actions;
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,26 @@ import { registerImporterCandidatureUseCase } from './importer/importerCandidatu
import { registerNotifierCandidatureCommand } from './notifier/notifierCandidature.command';
import { registerNotifierCandidatureUseCase } from './notifier/notifierCandidature.usecase';
import { registerConsulterDétailCandidatureQuery } from './détail/consulter/consulterDétailCandidature.query';
import {
ListerFournisseursÀLaCandidatureQueryDependencies,
registerListerFournisseursÀLaCandidatureQuery,
} from './lister/listerFournisseursÀLaCandidature.query';

export type CandiatureCommandDependencies = {
getProjetAggregateRoot: GetProjetAggregateRoot;
};

export type CandidatureQueryDependencies = ListerProjetsEligiblesPreuveRecanditureDependencies &
ConsulterCandidatureDependencies &
ListerCandidaturesQueryDependencies;
ListerCandidaturesQueryDependencies &
ListerFournisseursÀLaCandidatureQueryDependencies;

export const registerCandidatureQueries = (dependencies: CandidatureQueryDependencies) => {
registerProjetsEligiblesPreuveRecanditureQuery(dependencies);
registerConsulterCandidatureQuery(dependencies);
registerConsulterDétailCandidatureQuery(dependencies);
registerListerCandidaturesQuery(dependencies);
registerListerFournisseursÀLaCandidatureQuery(dependencies);
};

export const registerCandidaturesUseCases = ({
Expand Down
9 changes: 9 additions & 0 deletions packages/domain/projet/src/candidature/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import {
ListerCandidaturesQuery,
ListerCandidaturesReadModel,
} from './lister/listerCandidatures.query';
import {
ListerFournisseursÀLaCandidatureQuery,
ListerFournisseursÀLaCandidatureReadModel,
} from './lister/listerFournisseursÀLaCandidature.query';
import {
ListerProjetsEligiblesPreuveRecanditureQuery,
ListerProjetsEligiblesPreuveRecanditureReadModel,
Expand All @@ -38,12 +42,14 @@ import { NotifierCandidatureUseCase } from './notifier/notifierCandidature.useca
export type CandidatureQuery =
| ListerCandidaturesQuery
| ListerProjetsEligiblesPreuveRecanditureQuery
| ListerFournisseursÀLaCandidatureQuery
| ConsulterCandidatureQuery
| ConsulterDétailCandidatureQuery;

export {
ListerProjetsEligiblesPreuveRecanditureQuery,
ListerCandidaturesQuery,
ListerFournisseursÀLaCandidatureQuery,
ConsulterCandidatureQuery,
ConsulterDétailCandidatureQuery,
};
Expand All @@ -52,6 +58,7 @@ export {
export {
ListerProjetsEligiblesPreuveRecanditureReadModel,
ListerCandidaturesReadModel,
ListerFournisseursÀLaCandidatureReadModel,
ConsulterCandidatureReadModel,
ConsulterDétailCandidatureReadModel,
};
Expand Down Expand Up @@ -103,3 +110,5 @@ export * as DétailCandidature from './détail/détailCandidature.valueType';

// Type
export * from './détail/détailCandidature.valueType';

export { fournisseursCandidatureDétailKeys } from './lister/listerFournisseursÀLaCandidature.query';
Loading
Loading