From ceca7dfb81cd335c3667cf75ca16259b99162f2f Mon Sep 17 00:00:00 2001 From: Laurent Paoletti Date: Tue, 5 Nov 2024 09:00:32 +0100 Subject: [PATCH] Restrict bsdd recipient according to their profile and subprofile --- .env.model | 5 +- .github/workflows/integration-tests.yml | 2 + Changelog.md | 9 +- back/src/__tests__/factories.ts | 50 +- back/src/__tests__/testWorkflow.ts | 2 + .../bsdd/__tests__/bsdd.integration.ts | 4 +- back/src/bsda/__tests__/factories.ts | 12 +- .../__tests__/bsds.bsdd.integration.ts | 19 +- back/src/bspaoh/__tests__/factories.ts | 6 +- back/src/common/validation.ts | 58 +- back/src/common/workflow.d.ts | 8 +- back/src/companies/companyProfilesRules.ts | 70 + .../forms/__tests__/validation.integration.ts | 8 +- .../examples/workflows/acheminementDirect.ts | 8 +- .../acheminementDirectTransporteurEtranger.ts | 8 +- back/src/forms/examples/workflows/annexe1.ts | 8 +- .../workflows/entreposageProvisoire.ts | 13 +- ...treposageProvisoireTransporteurEtranger.ts | 13 +- .../examples/workflows/importBsdPapier.ts | 7 +- .../forms/examples/workflows/multiModal.ts | 7 +- .../multiModalTransporteurEtranger.ts | 7 +- .../forms/examples/workflows/multiModalv2.ts | 7 +- .../forms/examples/workflows/regroupement.ts | 13 +- .../regroupementTransporteurEtranger.ts | 13 +- .../mutations/__tests__/companyProfiles.ts | 52 + .../__tests__/createForm.integration.ts | 204 ++- .../__tests__/importPaperForm.integration.ts | 79 +- .../__tests__/markAsResealed.integration.ts | 112 +- .../__tests__/markAsResent.integration.ts | 21 +- .../__tests__/markAsSealed.integration.ts | 113 +- .../__tests__/updateForm.integration.ts | 1199 +++++++++++++++-- .../resolvers/mutations/duplicateForm.ts | 4 + .../__tests__/appendixforms.integration.ts | 8 +- back/src/forms/typeDefs/bsdd.inputs.graphql | 12 +- back/src/forms/validation.ts | 2 + libs/back/env/src/index.ts | 7 +- 36 files changed, 1969 insertions(+), 201 deletions(-) create mode 100644 back/src/companies/companyProfilesRules.ts create mode 100644 back/src/forms/resolvers/mutations/__tests__/companyProfiles.ts diff --git a/.env.model b/.env.model index b3dfc6ced4..6f4bf9db93 100644 --- a/.env.model +++ b/.env.model @@ -230,4 +230,7 @@ GERICO_API_KEY="***" # Slug to receive gerico webhooks GERICO_WEBHOOK_SLUG="/lorem" # Token to authenticate gerico webhooks -GERICO_WEBHOOK_TOKEN="***" \ No newline at end of file +GERICO_WEBHOOK_TOKEN="***" + +# For testing purposes, optional and defaulted to november 2024 release date: "2024-11-13T00:00:00.000Z" +VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER="2024-10-26:00:00.000Z" diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 91a9cd63b2..a00e22bb75 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -119,3 +119,5 @@ env: S3_SECRET_ACCESS_KEY: password S3_REGISTRY_ERRORS_BUCKET: registry-errors-integration S3_REGISTRY_IMPORTS_BUCKET: registry-imports-integration + VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER: 2024-10-30T00:00:00.000Z + diff --git a/Changelog.md b/Changelog.md index 2ff972329c..c8a4e0fdb4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,18 +4,14 @@ Les changements importants de Trackdéchets sont documentés dans ce fichier. Le format est basé sur [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), et le projet suit un schéma de versionning inspiré de [Calendar Versioning](https://calver.org/). -[2024.11.1] 19/11/2024 - -#### :house: Interne -- Amélioration de l'interface d'admin [PR 3735](https://github.com/MTES-MCT/trackdechets/pull/3735) - -# [2024.11.1] 19/11/2024 +[2024.11.1] 19/11/2024 #### :boom: Breaking changes - Le champ "Numéro de notification" est obligatoire lorsque la destination ultérieure renseignée est étrangère [PR 3719](https://github.com/MTES-MCT/trackdechets/pull/3719) - La présence d'une quantité reçue est requise pour passer du statut SENT à ACCEPTED via la mutation markAsReceived [PR 3720](https://github.com/MTES-MCT/trackdechets/pull/3720) +- Restriction des TTR et Installations de traitement à être visés sur un BSDD selon leur type de profil [PR 3725](https://github.com/MTES-MCT/trackdechets/pull/3725) #### :bug: Corrections de bugs @@ -23,6 +19,7 @@ et le projet suit un schéma de versionning inspiré de [Calendar Versioning](ht #### :house: Interne +- Amélioration de l'interface d'admin [PR 3735](https://github.com/MTES-MCT/trackdechets/pull/3735) - Modification de la query controlBsds et fermeture de la query bsds aux comptes gouvernementaux [PR 3270](https://github.com/MTES-MCT/trackdechets/pull/3270) # [2024.10.1] 22/10/2024 diff --git a/back/src/__tests__/factories.ts b/back/src/__tests__/factories.ts index 0aeae614c8..617f7e3591 100644 --- a/back/src/__tests__/factories.ts +++ b/back/src/__tests__/factories.ts @@ -12,7 +12,10 @@ import { Prisma, Company, TransportMode, - UserNotification + UserNotification, + WasteProcessorType, + CollectorType, + CompanyType } from "@prisma/client"; import { prisma } from "@td/prisma"; import { hashToken } from "../utils"; @@ -193,6 +196,9 @@ export const destinationFactory = async ( ...companyOpts, companyTypes: { set: [CompanyType.WASTEPROCESSOR] + }, + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] } } ); @@ -414,7 +420,17 @@ export const bsddTransporterFactory = async ({ return transporter; }; -export const upsertBaseSiret = async siret => { +export const upsertBaseSiret = async ({ + siret, + companyTypes = ["TRANSPORTER", "WASTEPROCESSOR", "WORKER"], + wasteProcessorTypes = [], + collectorTypes = [] +}: { + siret: string; + companyTypes?: CompanyType[]; + wasteProcessorTypes?: WasteProcessorType[]; + collectorTypes?: CollectorType[]; +}) => { const exists = await prisma.company.findUnique({ where: { siret } }); if (!exists) { // Using prisma.upsert gives us "Unique constraint failed on the fields: (`siret`)" @@ -425,7 +441,13 @@ export const upsertBaseSiret = async siret => { orgId: siret, siret, companyTypes: { - set: ["TRANSPORTER", "WASTEPROCESSOR", "WORKER"] + set: companyTypes + }, + wasteProcessorTypes: { + set: wasteProcessorTypes + }, + collectorTypes: { + set: collectorTypes }, name: `company_${siret}`, securityCode: 1234, @@ -450,10 +472,16 @@ export const formFactory = async ({ opt?: Partial; }) => { // Those sirets are required for the form to be updatable - await upsertBaseSiret( - (formdata.transporters!.create! as any).transporterCompanySiret as any - ); - await upsertBaseSiret(formdata.recipientCompanySiret); + + await upsertBaseSiret({ + siret: (formdata.transporters!.create! as any).transporterCompanySiret + }); + + // recipient needs appropriate profiles and subprofiles + await upsertBaseSiret({ + siret: formdata.recipientCompanySiret!, + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const ownerCompanies = await getUserCompanies(ownerId); const ownerOrgIds = ownerCompanies.map(company => company.orgId); @@ -513,10 +541,10 @@ export const formWithTempStorageFactory = async ({ opt?: Partial; forwardedInOpts?: Partial; }) => { - await upsertBaseSiret( - (forwardedInData.transporters?.create as any).transporterCompanySiret - ); - await upsertBaseSiret(forwardedInData.recipientCompanySiret); + await upsertBaseSiret({ + siret: (forwardedInData.transporters?.create as any).transporterCompanySiret + }); + await upsertBaseSiret({ siret: forwardedInData.recipientCompanySiret! }); const forwardedCreateInput: Omit< Prisma.FormCreateInput, diff --git a/back/src/__tests__/testWorkflow.ts b/back/src/__tests__/testWorkflow.ts index 4317e28461..c31f0df287 100644 --- a/back/src/__tests__/testWorkflow.ts +++ b/back/src/__tests__/testWorkflow.ts @@ -15,6 +15,8 @@ async function testWorkflow(workflow: Workflow) { for (const workflowCompany of workflow.companies) { const { user, company } = await userWithCompanyFactory("MEMBER", { companyTypes: workflowCompany.companyTypes, + wasteProcessorTypes: workflowCompany.wasteProcessorTypes ?? [], + collectorTypes: workflowCompany.collectorTypes ?? [], ...(workflowCompany?.opt || {}) }); if (workflowCompany.companyTypes.includes("ECO_ORGANISME")) { diff --git a/back/src/activity-events/bsdd/__tests__/bsdd.integration.ts b/back/src/activity-events/bsdd/__tests__/bsdd.integration.ts index 0de957af31..914d241fd2 100644 --- a/back/src/activity-events/bsdd/__tests__/bsdd.integration.ts +++ b/back/src/activity-events/bsdd/__tests__/bsdd.integration.ts @@ -14,6 +14,7 @@ import { import makeClient from "../../../__tests__/testClient"; import { getStream } from "../../data"; import { getFirstTransporterSync } from "../../../forms/database"; +import { WasteProcessorType, CompanyType } from "@prisma/client"; const CREATE_FORM = ` mutation CreateForm($createFormInput: CreateFormInput!) { @@ -55,7 +56,8 @@ describe("ActivityEvent.Bsdd", () => { const { company: destinationCompany } = await userWithCompanyFactory( "MEMBER", { - companyTypes: { set: ["WASTEPROCESSOR", "TRANSPORTER"] } + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] } ); const transporterCompany = await companyFactory({ diff --git a/back/src/bsda/__tests__/factories.ts b/back/src/bsda/__tests__/factories.ts index cc2c91c80b..93059939da 100644 --- a/back/src/bsda/__tests__/factories.ts +++ b/back/src/bsda/__tests__/factories.ts @@ -39,14 +39,14 @@ export const bsdaFactory = async ({ contactPhone: bsdaObject.emitterCompanyPhone ?? undefined, contactEmail: bsdaObject.emitterCompanyMail ?? undefined }); - await upsertBaseSiret( - ( + await upsertBaseSiret({ + siret: ( bsdaObject.transporters! .create! as Prisma.BsdaTransporterCreateWithoutBsdaInput - ).transporterCompanySiret // Prisma.BsdaTransporterCreateWithoutBsdaInput[] is wrongly infered - ); - await upsertBaseSiret(bsdaObject.destinationCompanySiret); - await upsertBaseSiret(bsdaObject.workerCompanySiret); + ).transporterCompanySiret! // Prisma.BsdaTransporterCreateWithoutBsdaInput[] is wrongly infered + }); + await upsertBaseSiret({ siret: bsdaObject.destinationCompanySiret! }); + await upsertBaseSiret({ siret: bsdaObject.workerCompanySiret! }); const data: Prisma.BsdaCreateInput = { ...bsdaObject, diff --git a/back/src/bsds/resolvers/queries/__tests__/bsds.bsdd.integration.ts b/back/src/bsds/resolvers/queries/__tests__/bsds.bsdd.integration.ts index cfff9fca8f..ee79395b31 100644 --- a/back/src/bsds/resolvers/queries/__tests__/bsds.bsdd.integration.ts +++ b/back/src/bsds/resolvers/queries/__tests__/bsds.bsdd.integration.ts @@ -1,4 +1,10 @@ -import { Company, User, UserRole } from "@prisma/client"; +import { + Company, + User, + UserRole, + WasteProcessorType, + CompanyType +} from "@prisma/client"; import { Query, QueryBsdsArgs, @@ -84,17 +90,17 @@ describe("Query.bsds workflow", () => { beforeAll(async () => { emitter = await userWithCompanyFactory(UserRole.ADMIN, { companyTypes: { - set: ["PRODUCER"] + set: [CompanyType.PRODUCER] } }); intermediary = await userWithCompanyFactory(UserRole.MEMBER, { companyTypes: { - set: ["TRANSPORTER"] + set: [CompanyType.TRANSPORTER] } }); transporter = await userWithCompanyFactory(UserRole.ADMIN, { companyTypes: { - set: ["TRANSPORTER"] + set: [CompanyType.TRANSPORTER] } }); await transporterReceiptFactory({ @@ -103,7 +109,10 @@ describe("Query.bsds workflow", () => { recipient = await userWithCompanyFactory(UserRole.ADMIN, { companyTypes: { - set: ["WASTEPROCESSOR"] + set: [CompanyType.WASTEPROCESSOR] + }, + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] } }); (searchCompany as jest.Mock).mockResolvedValue({ diff --git a/back/src/bspaoh/__tests__/factories.ts b/back/src/bspaoh/__tests__/factories.ts index 8730b4ad3e..35cfbe9d98 100644 --- a/back/src/bspaoh/__tests__/factories.ts +++ b/back/src/bspaoh/__tests__/factories.ts @@ -85,9 +85,9 @@ export const bspaohFactory = async ({ const destinationCompanySiret = siretify(2); const transporterCompanySiret = siretify(3); - await upsertBaseSiret(emitterCompanySiret); - await upsertBaseSiret(destinationCompanySiret); - await upsertBaseSiret(transporterCompanySiret); + await upsertBaseSiret({ siret: emitterCompanySiret }); + await upsertBaseSiret({ siret: destinationCompanySiret }); + await upsertBaseSiret({ siret: transporterCompanySiret }); await prisma.company.update({ where: { siret: destinationCompanySiret }, diff --git a/back/src/common/validation.ts b/back/src/common/validation.ts index cb46ef0645..05f78f4383 100644 --- a/back/src/common/validation.ts +++ b/back/src/common/validation.ts @@ -14,10 +14,19 @@ import { } from "../companies/validation"; import { CompanyInput } from "../generated/graphql/types"; import { prisma } from "@td/prisma"; -import { isForeignVat, isFRVat, isSiret, isVat } from "@td/constants"; +import { + isForeignVat, + isFRVat, + isSiret, + isVat, + isDangerous +} from "@td/constants"; import { isBase64 } from "../utils"; import { Decimal } from "decimal.js"; - +import { + canProcessDangerousWaste, + canProcessNonDangerousWaste +} from "../companies/companyProfilesRules"; // Poids maximum en tonnes tout mode de transport confondu export const MAX_WEIGHT_TONNES = 50000; @@ -176,6 +185,7 @@ type SiretTests = { role?: "DESTINATION" | "TRANSPORTER" | "WASTE_VEHICLES" ) => yup.TestConfig; isNotDormant: yup.TestConfig; + destinationHasAppropriateSubProfiles: yup.TestConfig; }; export const siretConditions: SiretConditions = { @@ -214,7 +224,14 @@ export const siretConditions: SiretConditions = { } }; -const { VERIFY_COMPANY } = process.env; +const { VERIFY_COMPANY, VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER } = + process.env; + +// Date de la MAJ 2024.11.1 qui rend obligatoire certtains sous profils pour traiter les déchets dangereux et non dangereux +const v20241101 = new Date( + VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER || + "2024-11-19T00:00:00.000Z" +); export const siretTests: SiretTests = { isRegistered: role => ({ @@ -302,6 +319,41 @@ export const siretTests: SiretTests = { } return company.isDormantSince == null; } + }, + destinationHasAppropriateSubProfiles: { + name: "destination-has-appropriate-subprofiles", + message: () => + "Les autorisations de l'établissement de destination ne semblent pas correspondre à la caractérisation du déchet " + + "renseigné. Merci de bien vouloir procéder à la mise à jour du profil de l'établissement ou modifier le type de " + + "déchet sans quoi le bordereau ne pourra être enregistré.", + test: async (siret, ctx) => { + if (!siret) return true; + + // do not run on existing bsdds created before release v20241101 + const bsddCreatedAt = ctx.parent.createdAt || new Date(); // new bsd do not have a createdAt yet + const isCreatedAfterV202411011 = + bsddCreatedAt.getTime() - v20241101.getTime() > 0; + + if (!isCreatedAfterV202411011) { + return true; + } + + const hasDangerousWaste = + isDangerous(ctx.parent.wasteDetailCode) || + ctx.parent.wasteDetailsPop || + ctx.parent.wasteDetailsIsDangerous; + + const company = await prisma.company.findUnique({ + where: { siret } + }); + if (company === null) { + return true; // catched by siretTests.isRegistered + } + + return hasDangerousWaste + ? canProcessDangerousWaste(company) + : canProcessNonDangerousWaste(company); + } } }; diff --git a/back/src/common/workflow.d.ts b/back/src/common/workflow.d.ts index da9fc8394d..fd6393853e 100644 --- a/back/src/common/workflow.d.ts +++ b/back/src/common/workflow.d.ts @@ -11,7 +11,13 @@ export type Workflow = { // Longer description of the workflow explaining the traceability use case; description?: string; // Name and profile of the companies involved in the workflow - companies: { name: string; companyTypes: string[]; opt?: any }[]; + companies: { + name: string; + companyTypes: string[]; + wasteProcessorTypes?: string[]; + collectorTypes?: string[]; + opt?: any; + }[]; // List of steps to be applied to the BSD steps: WorkflowStep[]; // Mocked context used in the documentation code examples diff --git a/back/src/companies/companyProfilesRules.ts b/back/src/companies/companyProfilesRules.ts new file mode 100644 index 0000000000..3f2361439f --- /dev/null +++ b/back/src/companies/companyProfilesRules.ts @@ -0,0 +1,70 @@ +import { CollectorType, WasteProcessorType, Company } from "@prisma/client"; + +const hasAuthorization = (requiredAuthorizations, companySubTypes): boolean => { + const matching = requiredAuthorizations.filter(i => + companySubTypes.includes(i) + ); + return !!matching.length; +}; + +export const canProcessDangerousWaste = (company: Company): boolean => { + const collectorAuthorizations = [ + CollectorType.DANGEROUS_WASTES, + CollectorType.DEEE_WASTES, + CollectorType.OTHER_DANGEROUS_WASTES + // NON_DANGEROUS_WASTES + // OTHER_NON_DANGEROUS_WASTES + ]; + const wasteProcessorAuthorizations = [ + WasteProcessorType.DANGEROUS_WASTES_INCINERATION, + WasteProcessorType.DANGEROUS_WASTES_STORAGE, + WasteProcessorType.NON_DANGEROUS_WASTES_STORAGE, + WasteProcessorType.OTHER_DANGEROUS_WASTES + // NON_DANGEROUS_WASTES_INCINERATION + // CREMATION + // INERT_WASTES_STORAGE + // OTHER_NON_DANGEROUS_WASTES + ]; + + const requiredAuthorizations = [ + ...collectorAuthorizations, + ...wasteProcessorAuthorizations + ]; + const companySubTypes = [ + ...company.collectorTypes, + ...company.wasteProcessorTypes + ]; + + return hasAuthorization(requiredAuthorizations, companySubTypes); +}; +export const canProcessNonDangerousWaste = (company: Company): boolean => { + const collectorAuthorizations = [ + CollectorType.NON_DANGEROUS_WASTES, + CollectorType.DEEE_WASTES, + CollectorType.OTHER_NON_DANGEROUS_WASTES + // DANGEROUS_WASTES + //OTHER_DANGEROUS_WASTES + ]; + + const wasteProcessorAuthorizations = [ + WasteProcessorType.DANGEROUS_WASTES_INCINERATION, + WasteProcessorType.NON_DANGEROUS_WASTES_INCINERATION, + WasteProcessorType.CREMATION, + WasteProcessorType.NON_DANGEROUS_WASTES_STORAGE, + WasteProcessorType.INERT_WASTES_STORAGE, + WasteProcessorType.OTHER_NON_DANGEROUS_WASTES + // DANGEROUS_WASTES_STORAGE + // OTHER_DANGEROUS_WASTES + ]; + + const requiredAuthorizations = [ + ...collectorAuthorizations, + ...wasteProcessorAuthorizations + ]; + const companySubTypes = [ + ...company.collectorTypes, + ...company.wasteProcessorTypes + ]; + + return hasAuthorization(requiredAuthorizations, companySubTypes); +}; diff --git a/back/src/forms/__tests__/validation.integration.ts b/back/src/forms/__tests__/validation.integration.ts index 983d74aec8..3e8adcf851 100644 --- a/back/src/forms/__tests__/validation.integration.ts +++ b/back/src/forms/__tests__/validation.integration.ts @@ -3,7 +3,8 @@ import { EmitterType, Form, OperationMode, - TransportMode + TransportMode, + WasteProcessorType } from "@prisma/client"; import { draftFormSchema, @@ -91,7 +92,10 @@ describe("sealedFormSchema", () => { companyTypes: ["TRANSPORTER"] }); const destinationCompany = await companyFactory({ - companyTypes: ["WASTEPROCESSOR"] + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } }); sealedForm = { ...formData, diff --git a/back/src/forms/examples/workflows/acheminementDirect.ts b/back/src/forms/examples/workflows/acheminementDirect.ts index 8de089fb0c..44eea875c2 100644 --- a/back/src/forms/examples/workflows/acheminementDirect.ts +++ b/back/src/forms/examples/workflows/acheminementDirect.ts @@ -5,7 +5,7 @@ import { markAsReceived } from "../steps/markAsReceived"; import { markAsProcessed } from "../steps/markAsProcessed"; import { signEmissionForm } from "../steps/signEmissionForm"; import { signTransportForm } from "../steps/signTransportForm"; - +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Acheminement direct du producteur à l'installation de traitement", description: `Les informations du BSDD sont remplies par le producteur du déchet. @@ -14,7 +14,11 @@ const workflow: Workflow = { companies: [ { name: "producteur", companyTypes: ["PRODUCER"] }, { name: "transporteur", companyTypes: ["TRANSPORTER"] }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createForm("producteur"), diff --git a/back/src/forms/examples/workflows/acheminementDirectTransporteurEtranger.ts b/back/src/forms/examples/workflows/acheminementDirectTransporteurEtranger.ts index cba0a4344e..4919b6fc3e 100644 --- a/back/src/forms/examples/workflows/acheminementDirectTransporteurEtranger.ts +++ b/back/src/forms/examples/workflows/acheminementDirectTransporteurEtranger.ts @@ -6,7 +6,7 @@ import { markAsProcessed } from "../steps/markAsProcessed"; import { signEmissionForm } from "../steps/signEmissionForm"; import { signTransportForm } from "../steps/signTransportForm"; import fixtures from "../fixturesForeignTransporter"; - +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Acheminement direct du producteur à l'installation de traitement avec un transporteur étranger", @@ -23,7 +23,11 @@ const workflow: Workflow = { vatNumber: "BE0541696005" } }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createForm("producteur", fixtures as any), diff --git a/back/src/forms/examples/workflows/annexe1.ts b/back/src/forms/examples/workflows/annexe1.ts index ca5f4c6163..b526ec3a16 100644 --- a/back/src/forms/examples/workflows/annexe1.ts +++ b/back/src/forms/examples/workflows/annexe1.ts @@ -12,7 +12,7 @@ import { groupAppendix1Producer, switchAppendixContext } from "../steps/updateForm"; - +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Bordereau chapeau et annexe 1", description: `Le collecteur crée un bordereau chapeau. Il crée ensuite ses bordereaux d'annexe 1 et les rattache à ce chapeau. @@ -20,7 +20,11 @@ const workflow: Workflow = { companies: [ { name: "collecteur", companyTypes: ["COLLECTOR", "TRANSPORTER"] }, { name: "producteur", companyTypes: ["PRODUCER"] }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createAppendix1Form("collecteur"), diff --git a/back/src/forms/examples/workflows/entreposageProvisoire.ts b/back/src/forms/examples/workflows/entreposageProvisoire.ts index d1e416ca1f..7bc29a63d4 100644 --- a/back/src/forms/examples/workflows/entreposageProvisoire.ts +++ b/back/src/forms/examples/workflows/entreposageProvisoire.ts @@ -13,6 +13,7 @@ import { signTransportForm, signTransportFormAfterTempStorage } from "../steps/signTransportForm"; +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Entreposage provisoire", @@ -26,9 +27,17 @@ de destination finale accepte le déchet et valide le traitement.`, companies: [ { name: "producteur", companyTypes: ["PRODUCER"] }, { name: "transporteur1", companyTypes: ["TRANSPORTER"] }, - { name: "ttr", companyTypes: ["COLLECTOR"] }, + { + name: "ttr", + companyTypes: ["COLLECTOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }, { name: "transporteur2", companyTypes: ["TRANSPORTER"] }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createFormTempStorage("producteur"), diff --git a/back/src/forms/examples/workflows/entreposageProvisoireTransporteurEtranger.ts b/back/src/forms/examples/workflows/entreposageProvisoireTransporteurEtranger.ts index 7e72ae28d4..8a434bd667 100644 --- a/back/src/forms/examples/workflows/entreposageProvisoireTransporteurEtranger.ts +++ b/back/src/forms/examples/workflows/entreposageProvisoireTransporteurEtranger.ts @@ -14,6 +14,7 @@ import { signTransportFormAfterTempStorage } from "../steps/signTransportForm"; import fixtures from "../fixturesForeignTransporter"; +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Entreposage provisoire avec transporteur étranger", @@ -31,13 +32,21 @@ de destination finale accepte le déchet et valide le traitement.`, companyTypes: ["TRANSPORTER"], opt: { siret: null, vatNumber: "BE0541696005" } }, - { name: "ttr", companyTypes: ["COLLECTOR"] }, + { + name: "ttr", + companyTypes: ["COLLECTOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }, { name: "transporteur2", companyTypes: ["TRANSPORTER"], opt: { siret: null, vatNumber: "IT13029381004" } }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createFormTempStorage("producteur", fixtures as any), diff --git a/back/src/forms/examples/workflows/importBsdPapier.ts b/back/src/forms/examples/workflows/importBsdPapier.ts index cc44069756..5eccc4167f 100644 --- a/back/src/forms/examples/workflows/importBsdPapier.ts +++ b/back/src/forms/examples/workflows/importBsdPapier.ts @@ -2,6 +2,7 @@ import { createForm } from "../steps/createForm"; import { markAsSealed } from "../steps/markAsSealed"; import { importPaperForm } from "../steps/importPaperForm"; import { Workflow } from "../../../common/workflow"; +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: `Acheminement direct du producteur à l'installation de traitement avec import de BSD signé papier.`, @@ -12,7 +13,11 @@ dans Trackdéchets par l'installation de destination pour assurer la traçabilit companies: [ { name: "producteur", companyTypes: ["PRODUCER"] }, { name: "transporteur", companyTypes: ["TRANSPORTER"] }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createForm("producteur"), diff --git a/back/src/forms/examples/workflows/multiModal.ts b/back/src/forms/examples/workflows/multiModal.ts index e6e83b4799..cd3075eaf3 100644 --- a/back/src/forms/examples/workflows/multiModal.ts +++ b/back/src/forms/examples/workflows/multiModal.ts @@ -8,6 +8,7 @@ import { markAsProcessed } from "../steps/markAsProcessed"; import { Workflow } from "../../../common/workflow"; import { signEmissionForm } from "../steps/signEmissionForm"; import { signTransportForm } from "../steps/signTransportForm"; +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Transport multi-modal", @@ -20,7 +21,11 @@ mis à jour au fur et mesure de la prise en charge du déchet sur les différent { name: "producteur", companyTypes: ["PRODUCER"] }, { name: "transporteur1", companyTypes: ["TRANSPORTER"] }, { name: "transporteur2", companyTypes: ["TRANSPORTER"] }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createFormMultiModal("producteur"), diff --git a/back/src/forms/examples/workflows/multiModalTransporteurEtranger.ts b/back/src/forms/examples/workflows/multiModalTransporteurEtranger.ts index 1be235edd5..ced6b54988 100644 --- a/back/src/forms/examples/workflows/multiModalTransporteurEtranger.ts +++ b/back/src/forms/examples/workflows/multiModalTransporteurEtranger.ts @@ -9,6 +9,7 @@ import { Workflow } from "../../../common/workflow"; import { signEmissionForm } from "../steps/signEmissionForm"; import { signTransportForm } from "../steps/signTransportForm"; import fixtures from "../fixturesForeignTransporter"; +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Transport multi-modal avec transporteurs étrangers", @@ -29,7 +30,11 @@ mis à jour au fur et mesure de la prise en charge du déchet sur les différent companyTypes: ["TRANSPORTER"], opt: { siret: null, vatNumber: "RO17579668" } }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createFormMultiModal("producteur", fixtures as any), diff --git a/back/src/forms/examples/workflows/multiModalv2.ts b/back/src/forms/examples/workflows/multiModalv2.ts index b19c7c4dd4..b9df8a7780 100644 --- a/back/src/forms/examples/workflows/multiModalv2.ts +++ b/back/src/forms/examples/workflows/multiModalv2.ts @@ -8,6 +8,7 @@ import { signTransportForm } from "../steps/signTransportForm"; import { createFormTransporter } from "../steps/createFormTransporter"; import { updateFormTransporterPlates } from "../steps/updateFormTransporter"; import { updateFormTransporters } from "../steps/updateForm"; +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: "Transport multi-modal (amélioration juillet 2023)", @@ -25,7 +26,11 @@ const workflow: Workflow = { { name: "transporteur1", companyTypes: ["TRANSPORTER"] }, { name: "transporteur2", companyTypes: ["TRANSPORTER"] }, { name: "transporteur3", companyTypes: ["TRANSPORTER"] }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ { diff --git a/back/src/forms/examples/workflows/regroupement.ts b/back/src/forms/examples/workflows/regroupement.ts index 42a63d9ac9..d412644174 100644 --- a/back/src/forms/examples/workflows/regroupement.ts +++ b/back/src/forms/examples/workflows/regroupement.ts @@ -5,6 +5,7 @@ import { markAsReceived } from "../steps/markAsReceived"; import { markAsAwaitingGroup } from "../steps/markAsProcessed"; import { signEmissionForm } from "../steps/signEmissionForm"; import { signTransportForm } from "../steps/signTransportForm"; +import { WasteProcessorType } from "@prisma/client"; const workflow: Workflow = { title: @@ -20,9 +21,17 @@ const workflow: Workflow = { companies: [ { name: "producteur", companyTypes: ["PRODUCER"] }, { name: "transporteur", companyTypes: ["TRANSPORTER"] }, - { name: "ttr", companyTypes: ["COLLECTOR"] }, + { + name: "ttr", + companyTypes: ["COLLECTOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }, { name: "transporteur2", companyTypes: ["TRANSPORTER"] }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createInitialForm("producteur"), diff --git a/back/src/forms/examples/workflows/regroupementTransporteurEtranger.ts b/back/src/forms/examples/workflows/regroupementTransporteurEtranger.ts index 3ca34c3db0..6d03f16bfd 100644 --- a/back/src/forms/examples/workflows/regroupementTransporteurEtranger.ts +++ b/back/src/forms/examples/workflows/regroupementTransporteurEtranger.ts @@ -6,6 +6,7 @@ import { markAsAwaitingGroup } from "../steps/markAsProcessed"; import { signEmissionForm } from "../steps/signEmissionForm"; import { signTransportForm } from "../steps/signTransportForm"; import fixtures from "../fixturesForeignTransporter"; +import { WasteProcessorType, CollectorType } from "@prisma/client"; const workflow: Workflow = { title: @@ -25,13 +26,21 @@ const workflow: Workflow = { companyTypes: ["TRANSPORTER"], opt: { siret: null, vatNumber: "BE0541696005" } }, - { name: "ttr", companyTypes: ["COLLECTOR"] }, + { + name: "ttr", + companyTypes: ["COLLECTOR"], + collectorTypes: [CollectorType.DANGEROUS_WASTES] + }, { name: "transporteur2", companyTypes: ["TRANSPORTER"], opt: { siret: null, vatNumber: "RO17579668" } }, - { name: "traiteur", companyTypes: ["WASTEPROCESSOR"] } + { + name: "traiteur", + companyTypes: ["WASTEPROCESSOR"], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ], steps: [ createInitialForm("producteur", fixtures as any), diff --git a/back/src/forms/resolvers/mutations/__tests__/companyProfiles.ts b/back/src/forms/resolvers/mutations/__tests__/companyProfiles.ts new file mode 100644 index 0000000000..7631fc774a --- /dev/null +++ b/back/src/forms/resolvers/mutations/__tests__/companyProfiles.ts @@ -0,0 +1,52 @@ +import { CompanyType, WasteProcessorType, CollectorType } from "@prisma/client"; + +export const forbbidenProfilesForDangerousWaste = [ + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [] + }, + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.NON_DANGEROUS_WASTES_INCINERATION] + }, + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.CREMATION] + }, + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.INERT_WASTES_STORAGE] + }, + { + companyTypes: [CompanyType.COLLECTOR], + collectorTypes: [CollectorType.NON_DANGEROUS_WASTES] + }, + { + companyTypes: [CompanyType.COLLECTOR], + collectorTypes: [CollectorType.OTHER_NON_DANGEROUS_WASTES] + } +]; + +export const forbbidenProfilesForNonDangerousWaste = [ + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [] + }, + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_STORAGE] + }, + + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.OTHER_DANGEROUS_WASTES] + }, + { + companyTypes: [CompanyType.COLLECTOR], + collectorTypes: [CollectorType.DANGEROUS_WASTES] + }, + { + companyTypes: [CompanyType.COLLECTOR], + collectorTypes: [CollectorType.OTHER_DANGEROUS_WASTES] + } +]; diff --git a/back/src/forms/resolvers/mutations/__tests__/createForm.integration.ts b/back/src/forms/resolvers/mutations/__tests__/createForm.integration.ts index f2b05b6d2d..28b1785e76 100644 --- a/back/src/forms/resolvers/mutations/__tests__/createForm.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/createForm.integration.ts @@ -25,7 +25,9 @@ import { EmitterType, Status, UserRole, - WasteAcceptationStatus + WasteAcceptationStatus, + CompanyType, + WasteProcessorType } from "@prisma/client"; import getReadableId from "../../../readableId"; import { sirenifyFormInput } from "../../../sirenify"; @@ -33,6 +35,11 @@ import { getFirstTransporterSync } from "../../../database"; import { updateAppendix2Queue } from "../../../../queue/producers/updateAppendix2"; import { waitForJobsCompletion } from "../../../../queue/helpers"; +import { + forbbidenProfilesForDangerousWaste, + forbbidenProfilesForNonDangerousWaste +} from "./companyProfiles"; + jest.mock("../../../sirenify"); (sirenifyFormInput as jest.Mock).mockImplementation(input => Promise.resolve(input) @@ -184,7 +191,8 @@ describe("Mutation.createForm", () => { ]); }); - it.each(["emitter", "trader", "recipient", "transporter"])( + it.each(["emitter", "trader", "transporter"])( + // recipient: see below "should allow %p to create a form", async role => { const { user, company } = await userWithCompanyFactory("MEMBER"); @@ -208,6 +216,176 @@ describe("Mutation.createForm", () => { } ); + it("should allow recipient to create a form", async () => { + // recipient needs appropriate profiles and subprofiles + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + + const { mutate } = makeClient(user); + const { data } = await mutate< + Pick, + MutationCreateFormArgs + >(CREATE_FORM, { + variables: { + createFormInput: { + recipient: { + company: { siret: company.siret } + } + } + } + }); + + expect(data.createForm.id).toBeTruthy(); + // check input is sirenified + expect(sirenifyFormInput as jest.Mock).toHaveBeenCalledTimes(1); + }); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient with inappropriate profile %o on a bsdd with dangerous waste code (*)", + async opt => { + // recipient needs appropriate profiles and subprofiles + const { user, company } = await userWithCompanyFactory("MEMBER", opt); + + const { mutate } = makeClient(user); + const { errors } = await mutate< + Pick, + MutationCreateFormArgs + >(CREATE_FORM, { + variables: { + createFormInput: { + wasteDetails: { code: "10 05 10*" }, + recipient: { + company: { siret: company.siret } + } + } + } + }); + + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient with inappropriate profile %o on a bsdd with non dangerous waste code and wasteDetailsIsDangerous", + async opt => { + // recipient needs appropriate profiles and subprofiles + const { user, company } = await userWithCompanyFactory("MEMBER", opt); + + const { mutate } = makeClient(user); + const { errors } = await mutate< + Pick, + MutationCreateFormArgs + >(CREATE_FORM, { + variables: { + createFormInput: { + wasteDetails: { code: "10 05 09", isDangerous: true }, + recipient: { + company: { siret: company.siret } + } + } + } + }); + + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient with inappropriate profile %o on a bsdd with non dangerous waste code and pop", + async opt => { + // recipient needs appropriate profiles and subprofiles + const { user, company } = await userWithCompanyFactory("MEMBER", opt); + + const { mutate } = makeClient(user); + const { errors } = await mutate< + Pick, + MutationCreateFormArgs + >(CREATE_FORM, { + variables: { + createFormInput: { + wasteDetails: { code: "10 05 09", pop: true }, + recipient: { + company: { siret: company.siret } + } + } + } + }); + + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForNonDangerousWaste)( + "should forbid recipient with inappropriate profile %o on a non dangerous bsdd ", + async opt => { + // recipient needs appropriate profiles and subprofiles + const { user, company } = await userWithCompanyFactory("MEMBER", opt); + + const { mutate } = makeClient(user); + const { errors } = await mutate< + Pick, + MutationCreateFormArgs + >(CREATE_FORM, { + variables: { + createFormInput: { + wasteDetails: { code: "10 05 09" }, // non dnagerous + recipient: { + company: { siret: company.siret } + } + } + } + }); + + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + it("should allow a transporter listed in the transporters list to create a form", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); @@ -398,7 +576,12 @@ describe("Mutation.createForm", () => { it("should allow to create a form without space in recipientProcessingOperation", async () => { const { user, company: emitter } = await userWithCompanyFactory("MEMBER"); const transporter = await companyFactory(); - const destination = await companyFactory(); + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); const { data, errors } = await mutate< @@ -765,7 +948,11 @@ describe("Mutation.createForm", () => { it("create a form with a recipient", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const { company: recipientCompany } = await userWithCompanyFactory( - "MEMBER" + "MEMBER", + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ); const createFormInput = { @@ -2109,6 +2296,11 @@ describe("Mutation.createForm", () => { it("should fill denormalized fields upon creation", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const recipient = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); const intermediaryCreation = toIntermediaryCompany(company); @@ -2124,7 +2316,7 @@ describe("Mutation.createForm", () => { company: { siret: company.siret } }, transporter: { company: { siret: company.siret } }, - recipient: { company: { siret: company.siret } }, + recipient: { company: { siret: recipient.siret } }, intermediaries: [intermediaryCreation] } } @@ -2134,7 +2326,7 @@ describe("Mutation.createForm", () => { where: { id: data.createForm.id } }); - expect(form.recipientsSirets).toContain(company.siret); + expect(form.recipientsSirets).toContain(recipient.siret); expect(form.transportersSirets).toContain(company.siret); expect(form.intermediariesSirets).toContain(company.siret); }); diff --git a/back/src/forms/resolvers/mutations/__tests__/importPaperForm.integration.ts b/back/src/forms/resolvers/mutations/__tests__/importPaperForm.integration.ts index 7418fbb8fa..c0468556b4 100644 --- a/back/src/forms/resolvers/mutations/__tests__/importPaperForm.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/importPaperForm.integration.ts @@ -5,7 +5,9 @@ import { Prisma, QuantityType, Status, - WasteAcceptationStatus + WasteAcceptationStatus, + CompanyType, + WasteProcessorType } from "@prisma/client"; import { format } from "date-fns"; import { prisma } from "@td/prisma"; @@ -43,7 +45,13 @@ describe("mutation / importPaperForm", () => { "MEMBER" ); const { company: recipientCompany } = await userWithCompanyFactory( - "MEMBER" + "MEMBER", + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [ + WasteProcessorType.DANGEROUS_WASTES_INCINERATION + ] + } ); const input = { @@ -127,7 +135,10 @@ describe("mutation / importPaperForm", () => { }); it("should import a BSD where user is recipient", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); @@ -166,7 +177,10 @@ describe("mutation / importPaperForm", () => { }); it("should fail if data is incomplete", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); @@ -189,7 +203,10 @@ describe("mutation / importPaperForm", () => { }); it("should import a form with an ecoOrganisme", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const ecoOrganisme = await prisma.ecoOrganisme.create({ data: { name: "EO", @@ -228,7 +245,10 @@ describe("mutation / importPaperForm", () => { }); it("should fail if eco-organisme is not known", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); @@ -257,7 +277,12 @@ describe("mutation / importPaperForm", () => { test.each(allowedFormats)( "%p should be a valid format form date fields", async f => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [ + WasteProcessorType.DANGEROUS_WASTES_INCINERATION + ] + }); const { mutate } = makeClient(user); @@ -295,7 +320,10 @@ describe("mutation / importPaperForm", () => { ); it("should set status to AWAITING_GROUP in case of groupement code", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); @@ -328,7 +356,10 @@ describe("mutation / importPaperForm", () => { }); it("should set status to NO_TREACEABILITY in case of no traceability", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); @@ -436,7 +467,10 @@ describe("mutation / importPaperForm", () => { it("should update a sealed form with imported data when user is recipient", async () => { const owner = await userFactory(); - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // create a form with a sealed status const form = await formFactory({ @@ -525,7 +559,10 @@ describe("mutation / importPaperForm", () => { it("should update as sealed form and overwrite existing data", async () => { const owner = await userFactory(); - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // create a form with a sealed status const form = await formFactory({ @@ -630,7 +667,10 @@ describe("mutation / importPaperForm", () => { it("should fail to update a sealed form if data is missing", async () => { const owner = await userFactory(); - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: owner.id, @@ -668,7 +708,10 @@ describe("mutation / importPaperForm", () => { it("should fail when trying to update a SIRET", async () => { const owner = await userFactory(); - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // create a form with a sealed status const form = await formFactory({ @@ -708,7 +751,10 @@ describe("mutation / importPaperForm", () => { it("should set status to AWAITING_GROUP in case of groupement code", async () => { const owner = await userFactory(); - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: owner.id, @@ -755,7 +801,10 @@ describe("mutation / importPaperForm", () => { it("should set status to NO_TRACEABILITY in case of no traceability", async () => { const owner = await userFactory(); - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: owner.id, diff --git a/back/src/forms/resolvers/mutations/__tests__/markAsResealed.integration.ts b/back/src/forms/resolvers/mutations/__tests__/markAsResealed.integration.ts index 40d24fd601..4195d32292 100644 --- a/back/src/forms/resolvers/mutations/__tests__/markAsResealed.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/markAsResealed.integration.ts @@ -11,7 +11,13 @@ import { userWithCompanyFactory } from "../../../../__tests__/factories"; import makeClient from "../../../../__tests__/testClient"; -import { CompanyType, CompanyVerificationStatus, Status } from "@prisma/client"; +import { + CompanyType, + CompanyVerificationStatus, + Status, + CollectorType, + WasteProcessorType +} from "@prisma/client"; import { Mutation, MutationMarkAsResealedArgs @@ -19,7 +25,10 @@ import { import { gql } from "graphql-tag"; import { sirenifyResealedFormInput } from "../../../sirenify"; import { getFirstTransporterSync } from "../../../database"; - +import { + forbbidenProfilesForDangerousWaste, + forbbidenProfilesForNonDangerousWaste +} from "./companyProfiles"; jest.mock("../../../sirenify"); (sirenifyResealedFormInput as jest.Mock).mockImplementation(input => Promise.resolve(input) @@ -355,11 +364,14 @@ describe("Mutation markAsResealed", () => { const owner = await userFactory(); const { user, company: collector } = await userWithCompanyFactory( "MEMBER", - { companyTypes: { set: [CompanyType.COLLECTOR] } } + { + companyTypes: { set: [CompanyType.COLLECTOR] } + } ); const destination = await companyFactory({ - companyTypes: [CompanyType.WASTEPROCESSOR] + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] }); const { mutate } = makeClient(user); @@ -539,6 +551,95 @@ describe("Mutation markAsResealed", () => { ]); }); + it.each(forbbidenProfilesForDangerousWaste)( + "should fail if destination after temp storage does not have appropriate subprofile (%o) when waste is dangerous", + async opt => { + const owner = await userFactory(); + const { user, company: collector } = await userWithCompanyFactory( + "MEMBER", + { + companyTypes: { set: [CompanyType.COLLECTOR] } + } + ); + + const { mutate } = makeClient(user); + + const destination = await companyFactory(opt); + + const form = await formWithTempStorageFactory({ + ownerId: owner.id, + opt: { + status: Status.TEMP_STORER_ACCEPTED, + recipientCompanySiret: collector.siret, + quantityReceived: 1 + }, + forwardedInOpts: { + recipientCompanySiret: destination.siret + } + }); + + const { errors } = await mutate(MARK_AS_RESEALED, { + variables: { + id: form.id, + resealedInfos: {} + } + }); + + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne pourra être enregistré." + }) + ]); + } + ); + + it.each(forbbidenProfilesForNonDangerousWaste)( + "should fail if destination after temp storage does not have appropriate subprofile (%o) whenwaste is not dangerous", + async opt => { + const owner = await userFactory(); + const { user, company: collector } = await userWithCompanyFactory( + "MEMBER", + { + companyTypes: { set: [CompanyType.COLLECTOR] } + } + ); + + const { mutate } = makeClient(user); + + const destination = await companyFactory(opt); + + const form = await formWithTempStorageFactory({ + ownerId: owner.id, + opt: { + status: Status.TEMP_STORER_ACCEPTED, + recipientCompanySiret: collector.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsIsDangerous: false, + wasteDetailsPop: false, + quantityReceived: 1 + }, + forwardedInOpts: { + recipientCompanySiret: destination.siret + } + }); + + const { errors } = await mutate(MARK_AS_RESEALED, { + variables: { + id: form.id, + resealedInfos: {} + } + }); + + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne pourra être enregistré." + }) + ]); + } + ); + it("should throw an error if VERIFY_COMPANY=true and destination after temp storage is not verified", async () => { // patch process.env and reload server process.env.VERIFY_COMPANY = "true"; @@ -687,7 +788,8 @@ describe("Mutation markAsResealed", () => { const { mutate } = makeClient(user); const destination = await companyFactory({ - companyTypes: [CompanyType.COLLECTOR] + companyTypes: [CompanyType.COLLECTOR], + collectorTypes: { set: [CollectorType.DANGEROUS_WASTES] } }); const form = await formWithTempStorageFactory({ diff --git a/back/src/forms/resolvers/mutations/__tests__/markAsResent.integration.ts b/back/src/forms/resolvers/mutations/__tests__/markAsResent.integration.ts index 181e2eab84..1b6525a196 100644 --- a/back/src/forms/resolvers/mutations/__tests__/markAsResent.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/markAsResent.integration.ts @@ -7,7 +7,7 @@ import { userWithCompanyFactory } from "../../../../__tests__/factories"; import makeClient from "../../../../__tests__/testClient"; -import { CompanyType } from "@prisma/client"; +import { CompanyType, WasteProcessorType } from "@prisma/client"; const MARK_AS_RESENT = ` mutation MarkAsResent($id: ID!, $resentInfos: ResentFormInput!){ @@ -24,11 +24,16 @@ describe("Mutation markAsResent", () => { test("it fails when form is not TEMP_STORER_ACCEPTED", async () => { const owner = await userFactory(); const { user, company } = await userWithCompanyFactory("MEMBER", { - companyTypes: { set: [CompanyType.COLLECTOR] } + companyTypes: { + set: [CompanyType.COLLECTOR] + } }); const { company: destination } = await userWithCompanyFactory("MEMBER", { - companyTypes: { set: [CompanyType.WASTEPROCESSOR] } + companyTypes: { set: [CompanyType.WASTEPROCESSOR] }, + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } }); const { mutate } = makeClient(user); @@ -65,7 +70,10 @@ describe("Mutation markAsResent", () => { }); const { company: destination } = await userWithCompanyFactory("MEMBER", { - companyTypes: { set: [CompanyType.WASTEPROCESSOR] } + companyTypes: { set: [CompanyType.WASTEPROCESSOR] }, + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } }); const { mutate } = makeClient(user); @@ -150,7 +158,10 @@ describe("Mutation markAsResent", () => { companyTypes: { set: [CompanyType.COLLECTOR] } }); const { company: destination } = await userWithCompanyFactory("MEMBER", { - companyTypes: { set: [CompanyType.WASTEPROCESSOR] } + companyTypes: { set: [CompanyType.WASTEPROCESSOR] }, + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } }); const { mutate } = makeClient(user); diff --git a/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts b/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts index 08ce4a7639..9f66c831e0 100644 --- a/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts @@ -1,4 +1,10 @@ -import { CompanyType, CompanyVerificationStatus, Status } from "@prisma/client"; +import { + CompanyType, + CompanyVerificationStatus, + Status, + WasteProcessorType, + CollectorType +} from "@prisma/client"; import { resetDatabase } from "../../../../../integration-tests/helper"; import { Mutation, @@ -55,14 +61,12 @@ describe("Mutation.markAsSealed", () => { it("should fail if SEALED is not a possible next state", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); - const destination = await destinationFactory(); const form = await formFactory({ ownerId: user.id, opt: { status: "SENT", - emitterCompanySiret: company.siret, - recipientCompanySiret: destination.siret + emitterCompanySiret: company.siret } }); @@ -79,6 +83,41 @@ describe("Mutation.markAsSealed", () => { ); }); + it("should fail if destination has inappropriate profile", async () => { + const { user, company } = await userWithCompanyFactory("MEMBER"); + const destination = await companyFactory(); + + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret + } + }); + + const { mutate } = makeClient(user); + const { errors } = await mutate(MARK_AS_SEALED, { + variables: { + id: form.id + } + }); + + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Erreur, impossible de valider le bordereau car des champs obligatoires ne sont pas renseignés.\n" + + "Erreur(s): Les autorisations de l'établissement de destination ne semblent pas correspondre à la " + + "caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à jour du profil de " + + "l'établissement ou modifier le type de déchet sans quoi le bordereau ne pourra être enregistré.", + + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + }); + it.each(["emitter", "recipient", "trader", "broker", "transporter"])( "%p of the BSD can seal it", async role => { @@ -92,7 +131,14 @@ describe("Mutation.markAsSealed", () => { }[role]); const { user, company } = await userWithCompanyFactory("MEMBER", { - companyTypes: { set: [companyType(role) as CompanyType] } + companyTypes: { set: [companyType(role) as CompanyType] }, + ...(role === "recipient" + ? { + wasteProcessorTypes: [ + WasteProcessorType.DANGEROUS_WASTES_INCINERATION + ] + } + : {}) }); const form = await formFactory({ @@ -382,7 +428,10 @@ describe("Mutation.markAsSealed", () => { it("the BSD can not be sealed if data do not validate", async () => { const { user, company } = await userWithCompanyFactory("MEMBER", { - companyTypes: { set: [CompanyType.WASTEPROCESSOR] } + companyTypes: { set: [CompanyType.WASTEPROCESSOR] }, + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } }); let form = await formFactory({ @@ -921,7 +970,6 @@ describe("Mutation.markAsSealed", () => { it("should mark appendix2 forms as grouped", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); - const destination = await destinationFactory(); const groupedForm1 = await formFactory({ ownerId: user.id, opt: { status: "AWAITING_GROUP", quantityReceived: 1 } @@ -943,7 +991,6 @@ describe("Mutation.markAsSealed", () => { status: "DRAFT", emitterType: "APPENDIX2", emitterCompanySiret: company.siret, - recipientCompanySiret: destination.siret, grouping: { create: [ { @@ -1209,7 +1256,10 @@ describe("Mutation.markAsSealed", () => { expect.objectContaining({ message: [ "Erreur, impossible de valider le bordereau car des champs obligatoires ne sont pas renseignés.", - `Erreur(s): L'installation de destination ou d’entreposage ou de reconditionnement avec le SIRET "${destination.siret}" n'est pas inscrite sur Trackdéchets en tant qu'installation de traitement ou de tri transit regroupement. Cette installation ne peut donc pas être visée sur le bordereau. Veuillez vous rapprocher de l'administrateur de cette installation pour qu'il modifie le profil de l'établissement depuis l'interface Trackdéchets dans Mes établissements` + `Erreur(s): L'installation de destination ou d’entreposage ou de reconditionnement avec le SIRET "${destination.siret}" n'est pas inscrite sur Trackdéchets en tant qu'installation de traitement ou de tri transit regroupement. Cette installation ne peut donc pas être visée sur le bordereau. Veuillez vous rapprocher de l'administrateur de cette installation pour qu'il modifie le profil de l'établissement depuis l'interface Trackdéchets dans Mes établissements`, + "Les autorisations de l'établissement de destination ne semblent pas correspondre à la caractérisation " + + "du déchet renseigné. Merci de bien vouloir procéder à la mise à jour du profil de l'établissement ou modifier " + + "le type de déchet sans quoi le bordereau ne pourra être enregistré." ].join("\n") }) ]); @@ -1220,7 +1270,8 @@ describe("Mutation.markAsSealed", () => { "MEMBER" ); const collector = await companyFactory({ - companyTypes: { set: [CompanyType.COLLECTOR] } + companyTypes: { set: [CompanyType.COLLECTOR] }, + collectorTypes: { set: [CollectorType.DANGEROUS_WASTES] } }); const form = await formWithTempStorageFactory({ @@ -1256,7 +1307,8 @@ describe("Mutation.markAsSealed", () => { "MEMBER" ); const collector = await companyFactory({ - companyTypes: { set: [CompanyType.COLLECTOR] } + companyTypes: { set: [CompanyType.COLLECTOR] }, + collectorTypes: { set: [CollectorType.DANGEROUS_WASTES] } }); const destination = await companyFactory({ // assume profile is not COLLECTOR or WASTEPROCESSOR @@ -1300,8 +1352,14 @@ describe("Mutation.markAsSealed", () => { ); const destination = await companyFactory({ companyTypes: { set: [CompanyType.WASTEPROCESSOR] }, + + wasteProcessorTypes: { + set: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }, + verificationStatus: CompanyVerificationStatus.TO_BE_VERIFIED }); + const form = await formFactory({ ownerId: user.id, opt: { @@ -1336,7 +1394,9 @@ describe("Mutation.markAsSealed", () => { "MEMBER" ); const collector = await companyFactory({ - companyTypes: { set: [CompanyType.COLLECTOR] }, + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION], + verificationStatus: CompanyVerificationStatus.VERIFIED }); const destination = await companyFactory({ @@ -1374,7 +1434,8 @@ describe("Mutation.markAsSealed", () => { const { user, company: destination } = await userWithCompanyFactory( "MEMBER", { - companyTypes: [CompanyType.WASTEPROCESSOR] + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] } ); const form = await formFactory({ @@ -1461,7 +1522,8 @@ describe("Mutation.markAsSealed", () => { companyTypes: [CompanyType.PRODUCER] }); const recipient = await companyFactory({ - companyTypes: [CompanyType.WASTEPROCESSOR] + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] }); const broker = await companyFactory(); @@ -1503,7 +1565,8 @@ describe("Mutation.markAsSealed", () => { companyTypes: [CompanyType.PRODUCER] }); const recipient = await companyFactory({ - companyTypes: [CompanyType.WASTEPROCESSOR] + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] }); const broker = await companyFactory(); @@ -1541,7 +1604,10 @@ describe("Mutation.markAsSealed", () => { }); it("should fail if bsd has a foreign ship and the wrong emitterType", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1573,7 +1639,10 @@ describe("Mutation.markAsSealed", () => { }); it("should fail if bsd has a private producer and the wrong emitterType", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1605,7 +1674,10 @@ describe("Mutation.markAsSealed", () => { }); it("should seal and automatically transition to SIGNED_BY_PRODUCER when private individual emitter", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1637,7 +1709,10 @@ describe("Mutation.markAsSealed", () => { }); it("should seal and automatically transition to SIGNED_BY_PRODUCER when foreign ship emitter", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, diff --git a/back/src/forms/resolvers/mutations/__tests__/updateForm.integration.ts b/back/src/forms/resolvers/mutations/__tests__/updateForm.integration.ts index 79ac69a44c..df1c67fcc0 100644 --- a/back/src/forms/resolvers/mutations/__tests__/updateForm.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/updateForm.integration.ts @@ -1,4 +1,10 @@ -import { EmitterType, Status, UserRole } from "@prisma/client"; +import { + EmitterType, + Status, + UserRole, + CompanyType, + WasteProcessorType +} from "@prisma/client"; import { resetDatabase } from "../../../../../integration-tests/helper"; import { prisma } from "@td/prisma"; import { ErrorCode } from "../../../../common/errors"; @@ -27,6 +33,10 @@ import { getFirstTransporter, getTransportersSync } from "../../../database"; import { getStream } from "../../../../activity-events"; import { updateAppendix2Queue } from "../../../../queue/producers/updateAppendix2"; import { waitForJobsCompletion } from "../../../../queue/helpers"; +import { + forbbidenProfilesForDangerousWaste, + forbbidenProfilesForNonDangerousWaste +} from "./companyProfiles"; jest.mock("../../../sirenify"); (sirenifyFormInput as jest.Mock).mockImplementation(input => @@ -247,7 +257,10 @@ describe("Mutation.updateForm", () => { it("should be possible for the TTR to resend the same data on the temporaryStorateDetail input", async () => { const emitter = await userWithCompanyFactory("ADMIN"); - const ttr = await userWithCompanyFactory("ADMIN"); + const ttr = await userWithCompanyFactory("ADMIN", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const destination = await companyFactory(); const form = await formWithTempStorageFactory({ @@ -279,7 +292,10 @@ describe("Mutation.updateForm", () => { it("should not be possible to update intermediaries when emitter has signed", async () => { const emitter = await userWithCompanyFactory("ADMIN"); - const destination = await userWithCompanyFactory("ADMIN"); + const destination = await userWithCompanyFactory("ADMIN", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const intermediary1 = await companyFactory(); const intermediary2 = await companyFactory(); const form = await formFactory({ @@ -339,7 +355,10 @@ describe("Mutation.updateForm", () => { it("should be possible to resend same intermediaries data when emitter has signed", async () => { const emitter = await userWithCompanyFactory("ADMIN"); - const destination = await userWithCompanyFactory("ADMIN"); + const destination = await userWithCompanyFactory("ADMIN", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const intermediary1 = await companyFactory(); const form = await formFactory({ ownerId: emitter.user.id, @@ -370,7 +389,10 @@ describe("Mutation.updateForm", () => { it("should be possible to resend same Decimal value when emitter has signed", async () => { const emitter = await userWithCompanyFactory("ADMIN"); - const destination = await userWithCompanyFactory("ADMIN"); + const destination = await userWithCompanyFactory("ADMIN", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const intermediary1 = await companyFactory(); const form = await formFactory({ ownerId: emitter.user.id, @@ -399,13 +421,21 @@ describe("Mutation.updateForm", () => { expect(errors).toBeUndefined(); }); - it.each(["emitter", "trader", "broker", "recipient", "transporter"])( + it.each(["emitter", "trader", "broker", "transporter"])( "should allow %p to update a draft form", async role => { const { user, company } = await userWithCompanyFactory("MEMBER"); + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { + recipientCompanySiret: destination.siret, status: "DRAFT", ...(role === "transporter" ? { @@ -432,6 +462,7 @@ describe("Mutation.updateForm", () => { const { data } = await mutate>(UPDATE_FORM, { variables: { updateFormInput } }); + expect(data.updateForm.wasteDetails).toMatchObject( updateFormInput.wasteDetails ); @@ -440,6 +471,290 @@ describe("Mutation.updateForm", () => { } ); + it("should allow recipient to update a draft form", async () => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + recipientCompanySiret: company.siret + } + }); + + const { mutate } = makeClient(user); + const updateFormInput = { + id: form.id, + wasteDetails: { + code: "01 01 01" + } + }; + const { data } = await mutate>(UPDATE_FORM, { + variables: { updateFormInput } + }); + expect(data.updateForm.wasteDetails).toMatchObject( + updateFormInput.wasteDetails + ); + // check input is sirenified + expect(sirenifyFormInput as jest.Mock).toHaveBeenCalledTimes(1); + }); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a draft bsdd with dangerous waste code", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterCompanySiret: company.siret + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a draft bsdd with non dangerous waste and isDangerous", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsIsDangerous: true + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a draft bsdd with non dangerous waste code and pop", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsPop: true + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForNonDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a draft bsdd with non dangerous waste", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsIsDangerous: false, + wasteDetailsPop: false + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should allow recipient with inappropriate profile %o on a draft bsdd with dangerous waste code on bsdd created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterCompanySiret: company.siret, + createdAt: new Date("2024-10-01T00:00:00.000Z") // created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toBe(undefined); + } + ); + + it.each(forbbidenProfilesForNonDangerousWaste)( + "should allow recipient with inappropriate profile %o on a draft bsdd with non dangerous waste on bsdd created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsIsDangerous: false, + wasteDetailsPop: false, + createdAt: new Date("2024-10-01T00:00:00.000Z") // created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toBe(undefined); + } + ); + it("should autocomplete transporter receipt with receipt pulled from db", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); @@ -449,11 +764,19 @@ describe("Mutation.updateForm", () => { await transporterReceiptFactory({ company: transporterCompany }); + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, transporters: { create: { transporterCompanySiret: company.siret, @@ -487,11 +810,19 @@ describe("Mutation.updateForm", () => { const transporterCompany = await companyFactory({ companyTypes: ["TRANSPORTER"] }); + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, transporters: { create: { transporterCompanySiret: company.siret, @@ -523,12 +854,20 @@ describe("Mutation.updateForm", () => { it("should let the transporter receipt unchanged if transporter is unchanged", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const receipt = await transporterReceiptFactory({ company }); - // form receipt is fileld + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + + // form receipt is filled const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, transporters: { create: { transporterCompanySiret: company.siret, @@ -560,64 +899,364 @@ describe("Mutation.updateForm", () => { ); }); - it.each(["emitter", "trader", "broker", "recipient", "transporter"])( - "should allow %p to update a sealed form", - async role => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + it.each(["emitter", "trader", "broker", "transporter"])( + "should allow %p to update a sealed form", + async role => { + const { user, company } = await userWithCompanyFactory("MEMBER"); + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + + const form = await formFactory({ + ownerId: user.id, + opt: { + recipientCompanySiret: destination.siret, + + status: "SEALED", + ...(role === "transporter" + ? { + transporters: { + create: { + [`${role}CompanySiret`]: company.siret, + number: 1 + } + } + } + : { + [`${role}CompanySiret`]: company.siret + }), + ...(["trader", "broker"].includes(role) + ? { + [`${role}CompanyName`]: "Trader or Broker", + [`${role}CompanyContact`]: "Mr Trader or Broker", + [`${role}CompanyMail`]: "traderbroker@trackdechets.fr", + [`${role}CompanyAddress`]: "Wall street", + [`${role}CompanyPhone`]: "00 00 00 00 00", + [`${role}Receipt`]: "receipt", + [`${role}Department`]: "07", + [`${role}ValidityLimit`]: new Date("2023-01-01") + } + : {}) + } + }); + + const { mutate } = makeClient(user); + const updateFormInput = { + id: form.id, + wasteDetails: { + code: "08 01 11*" + } + }; + const { data } = await mutate>(UPDATE_FORM, { + variables: { updateFormInput } + }); + + expect(data.updateForm.wasteDetails).toMatchObject( + updateFormInput.wasteDetails + ); + } + ); + + it("should allow recipient to update a sealed form", async () => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "SEALED", + + recipientCompanySiret: company.siret + } + }); + + const { mutate } = makeClient(user); + const updateFormInput = { + id: form.id, + wasteDetails: { + code: "08 01 11*" + } + }; + const { data } = await mutate>(UPDATE_FORM, { + variables: { updateFormInput } + }); + + expect(data.updateForm.wasteDetails).toMatchObject( + updateFormInput.wasteDetails + ); + }); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a sealed bsdd with dangerous waste code", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "SEALED", + emitterCompanySiret: company.siret + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a SEALED bsdd with non dangerous waste and isDangerous", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "SEALED", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsIsDangerous: true + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a sealed bsdd with non dangerous waste code and pop", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "SEALED", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsPop: true + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForNonDangerousWaste)( + "should forbid recipient without inappropriate profile %o on a sealed bsdd with non dangerous waste", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "SEALED", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsIsDangerous: false, + wasteDetailsPop: false + } + }); + + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toEqual([ + expect.objectContaining({ + message: + "Les autorisations de l'établissement de destination ne semblent pas correspondre " + + "à la caractérisation du déchet renseigné. Merci de bien vouloir procéder à la mise à " + + "jour du profil de l'établissement ou modifier le type de déchet sans quoi le bordereau ne " + + "pourra être enregistré.", + extensions: { + code: "BAD_USER_INPUT" + } + }) + ]); + } + ); + + it.each(forbbidenProfilesForDangerousWaste)( + "should allow recipient with inappropriate profile %o on a sealed bsdd with dangerous waste code on bsdd created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "SEALED", - ...(role === "transporter" - ? { - transporters: { - create: { - [`${role}CompanySiret`]: company.siret, - number: 1 - } - } - } - : { - [`${role}CompanySiret`]: company.siret - }), - ...(["trader", "broker"].includes(role) - ? { - [`${role}CompanyName`]: "Trader or Broker", - [`${role}CompanyContact`]: "Mr Trader or Broker", - [`${role}CompanyMail`]: "traderbroker@trackdechets.fr", - [`${role}CompanyAddress`]: "Wall street", - [`${role}CompanyPhone`]: "00 00 00 00 00", - [`${role}Receipt`]: "receipt", - [`${role}Department`]: "07", - [`${role}ValidityLimit`]: new Date("2023-01-01") - } - : {}) + emitterCompanySiret: company.siret, + createdAt: new Date("2024-10-01T00:00:00.000Z") // created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER } }); const { mutate } = makeClient(user); + const destination = await companyFactory(opt); const updateFormInput = { id: form.id, - wasteDetails: { - code: "08 01 11*" + recipient: { + company: { siret: destination.siret } } }; - const { data } = await mutate>(UPDATE_FORM, { - variables: { updateFormInput } + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } + ); + expect(errors).toBe(undefined); + } + ); + + it.each(forbbidenProfilesForNonDangerousWaste)( + "should allow recipient with inappropriate profile %o on a sealed bsdd with non dangerous waste on bsdd created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER", + async opt => { + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "SEALED", + emitterCompanySiret: company.siret, + wasteDetailsCode: "10 05 09", + wasteDetailsIsDangerous: false, + wasteDetailsPop: false, + createdAt: new Date("2024-10-01T00:00:00.000Z") // created before VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER + } }); - expect(data.updateForm.wasteDetails).toMatchObject( - updateFormInput.wasteDetails + const { mutate } = makeClient(user); + const destination = await companyFactory(opt); + const updateFormInput = { + id: form.id, + recipient: { + company: { siret: destination.siret } + } + }; + const { errors } = await mutate>( + UPDATE_FORM, + { + variables: { updateFormInput } + } ); + expect(errors).toBe(undefined); } ); it("should allow a destination after temp storage to update a form", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formWithTempStorageFactory({ ownerId: user.id, - opt: { status: "DRAFT" }, + opt: { status: "DRAFT", recipientCompanySiret: destination.siret }, forwardedInOpts: { recipientCompanySiret: company.siret } }); @@ -715,13 +1354,21 @@ describe("Mutation.updateForm", () => { siret: eo.siret! } }); + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, + opt: { status: "DRAFT", emitterType: "OTHER", ecoOrganismeName: eo.name, - ecoOrganismeSiret: eo.siret + ecoOrganismeSiret: eo.siret, + recipientCompanySiret: destination.siret } }); @@ -805,6 +1452,12 @@ describe("Mutation.updateForm", () => { it("should update the eco-organisme", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const originalEO = await companyFactory({ companyTypes: { set: ["ECO_ORGANISME"] @@ -823,7 +1476,8 @@ describe("Mutation.updateForm", () => { status: "DRAFT", emitterCompanySiret: company.siret, ecoOrganismeName: originalEO.name, - ecoOrganismeSiret: originalEO.siret + ecoOrganismeSiret: originalEO.siret, + recipientCompanySiret: destination.siret } }); const newEO = await companyFactory({ @@ -872,13 +1526,19 @@ describe("Mutation.updateForm", () => { siret: eo.siret! } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, ecoOrganismeName: eo.name, - ecoOrganismeSiret: eo.siret + ecoOrganismeSiret: eo.siret, + recipientCompanySiret: destination.siret } }); @@ -898,12 +1558,17 @@ describe("Mutation.updateForm", () => { it("should add the first intermediary on an existing draft", async () => { const { user, company } = await userWithCompanyFactory(UserRole.MEMBER); const intermediary = await userWithCompanyFactory(UserRole.MEMBER); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", - emitterCompanySiret: company.siret + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret } }); @@ -930,11 +1595,18 @@ describe("Mutation.updateForm", () => { const intermediary = await userWithCompanyFactory(UserRole.MEMBER); const intermediary2 = await userWithCompanyFactory(UserRole.MEMBER); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, intermediaries: { create: [toIntermediaryCompany(intermediary.company)] } @@ -994,12 +1666,17 @@ describe("Mutation.updateForm", () => { it("should remove the intermediary when input is an empty array", async () => { const { user, company } = await userWithCompanyFactory(UserRole.MEMBER); const intermediary = await userWithCompanyFactory(UserRole.MEMBER); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, intermediaries: { create: [toIntermediaryCompany(intermediary.company)] } @@ -1025,12 +1702,17 @@ describe("Mutation.updateForm", () => { it("should remove the intermediary when input intermediaries is null", async () => { const { user, company } = await userWithCompanyFactory(UserRole.MEMBER); const intermediary = await userWithCompanyFactory(UserRole.MEMBER); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, intermediaries: { create: [toIntermediaryCompany(intermediary.company)] } @@ -1056,12 +1738,17 @@ describe("Mutation.updateForm", () => { it("should not update the intermediary when no input", async () => { const { user, company } = await userWithCompanyFactory(UserRole.MEMBER); const intermediary = await userWithCompanyFactory(UserRole.MEMBER); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, intermediaries: { create: [toIntermediaryCompany(intermediary.company)] } @@ -1091,11 +1778,17 @@ describe("Mutation.updateForm", () => { it("should update a form as an intermediary", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const intermediary = await userWithCompanyFactory(UserRole.MEMBER); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "SEALED", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, intermediaries: { create: [toIntermediaryCompany(intermediary.company)] } @@ -1125,11 +1818,17 @@ describe("Mutation.updateForm", () => { it("should update a form", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", - emitterCompanySiret: company.siret + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret } }); @@ -1150,11 +1849,17 @@ describe("Mutation.updateForm", () => { it("should update a form with a PIPELINE packaging, erasing transporter infos and forcing transporter mode OTHER", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", - emitterCompanySiret: company.siret + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret } }); @@ -1226,11 +1931,17 @@ describe("Mutation.updateForm", () => { it("should add a temporary storage", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", - emitterCompanySiret: company.siret + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret } }); @@ -1259,11 +1970,17 @@ describe("Mutation.updateForm", () => { it("should add a temporary storage even if temporaryStorageDetail is empty ", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", - emitterCompanySiret: company.siret + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret } }); @@ -1291,12 +2008,18 @@ describe("Mutation.updateForm", () => { it("should update the temporary storage", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, recipientIsTempStorage: true, + recipientCompanySiret: destination.siret, forwardedIn: { create: { readableId: getReadableId(), @@ -1329,11 +2052,17 @@ describe("Mutation.updateForm", () => { it("should remove a temporary storage", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, forwardedIn: { create: { readableId: getReadableId(), @@ -1360,14 +2089,23 @@ describe("Mutation.updateForm", () => { it("should add a recipient", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", - emitterCompanySiret: company.siret + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret } }); - const newRecipientCompany = await companyFactory(); + const newRecipientCompany = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const { mutate } = makeClient(user); const updateFormInput = { @@ -1391,7 +2129,12 @@ describe("Mutation.updateForm", () => { it("should update the recipient", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); - const originalRecipientCompany = await companyFactory(); + // recipient needs appropriate profiles and subprofiles + const originalRecipientCompany = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { @@ -1400,8 +2143,11 @@ describe("Mutation.updateForm", () => { recipientCompanySiret: originalRecipientCompany.siret } }); - const newRecipientCompany = await companyFactory(); + const newRecipientCompany = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const updateFormInput = { id: form.id, recipient: { @@ -1513,6 +2259,11 @@ describe("Mutation.updateForm", () => { quantityGrouped: 0 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1520,6 +2271,8 @@ describe("Mutation.updateForm", () => { emitterType: EmitterType.APPENDIX2, status: "SEALED", emitterCompanySiret: ttr.siret, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendixForm.id, @@ -1575,6 +2328,11 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1582,6 +2340,8 @@ describe("Mutation.updateForm", () => { status: "SEALED", emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendixForm.id, @@ -1620,6 +2380,11 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: ttrUser.id, @@ -1627,6 +2392,8 @@ describe("Mutation.updateForm", () => { status: "SEALED", emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendixForm.id, @@ -1676,6 +2443,11 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1683,6 +2455,8 @@ describe("Mutation.updateForm", () => { status: Status.SEALED, emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: initialAppendix2.id, @@ -1729,6 +2503,11 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1736,6 +2515,8 @@ describe("Mutation.updateForm", () => { status: Status.SEALED, emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: initialAppendix2.id, @@ -1785,6 +2566,11 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1792,6 +2578,8 @@ describe("Mutation.updateForm", () => { status: "DRAFT", emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendixForm.id, @@ -1836,13 +2624,19 @@ describe("Mutation.updateForm", () => { recipientCompanySiret: producer.siret } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: producer.siret, - emitterType: EmitterType.PRODUCER + emitterType: EmitterType.PRODUCER, + recipientCompanySiret: destination.siret } }); @@ -1884,12 +2678,19 @@ describe("Mutation.updateForm", () => { } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: producer.siret, - emitterType: EmitterType.PRODUCER + emitterType: EmitterType.PRODUCER, + recipientCompanySiret: destination.siret } }); @@ -1930,6 +2731,11 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1937,6 +2743,8 @@ describe("Mutation.updateForm", () => { status: "DRAFT", emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendixForm.id, @@ -1974,6 +2782,11 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -1981,6 +2794,8 @@ describe("Mutation.updateForm", () => { status: "DRAFT", emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendixForm.id, @@ -2019,13 +2834,19 @@ describe("Mutation.updateForm", () => { quantityGrouped: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "SEALED", emitterCompanySiret: ttr.siret, - emitterType: EmitterType.APPENDIX2 + emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret } }); @@ -2076,13 +2897,19 @@ describe("Mutation.updateForm", () => { quantityReceived: 1 } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const groupingForm1 = await formFactory({ ownerId: user.id, opt: { status: "SEALED", emitterCompanySiret: ttr.siret, - emitterType: EmitterType.APPENDIX2 + emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret } }); @@ -2091,7 +2918,8 @@ describe("Mutation.updateForm", () => { opt: { status: "SEALED", emitterCompanySiret: ttr.siret, - emitterType: EmitterType.APPENDIX2 + emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret } }); @@ -2159,6 +2987,8 @@ describe("Mutation.updateForm", () => { }); it("should default to quantity left when no quantity is specified in grouping", async () => { + // recipient needs appropriate profiles and subprofiles + const { user, company: ttr } = await userWithCompanyFactory("MEMBER"); const appendix2Form = await formFactory({ @@ -2183,13 +3013,19 @@ describe("Mutation.updateForm", () => { } } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const groupingForm = await formFactory({ ownerId: user.id, opt: { status: "SEALED", emitterCompanySiret: ttr.siret, - emitterType: EmitterType.APPENDIX2 + emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret } }); @@ -2243,13 +3079,20 @@ describe("Mutation.updateForm", () => { it("should be possible to set isDangerous=true with a waste code without *", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { status: "DRAFT", emitterCompanySiret: company.siret, wasteDetailsCode: "20 03 01", - wasteDetailsIsDangerous: false + wasteDetailsIsDangerous: false, + recipientCompanySiret: destination.siret } }); @@ -2268,6 +3111,11 @@ describe("Mutation.updateForm", () => { it("should perform update in transaction", async () => { const { user, company: ttr } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, @@ -2275,7 +3123,8 @@ describe("Mutation.updateForm", () => { status: "SEALED", emitterCompanySiret: ttr.siret, emitterType: EmitterType.APPENDIX2, - wasteDetailsCode: "01 03 04*" + wasteDetailsCode: "01 03 04*", + recipientCompanySiret: destination.siret } }); @@ -2311,10 +3160,19 @@ describe("Mutation.updateForm", () => { }); it("should update denormalized fields", async () => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const transporterCompany = await companyFactory({ companyTypes: ["TRANSPORTER"] }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + await transporterReceiptFactory({ company: transporterCompany }); const intermediaryCompany = await companyFactory(); @@ -2326,7 +3184,8 @@ describe("Mutation.updateForm", () => { status: "SEALED", emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX2, - wasteDetailsCode: "01 03 04*" + wasteDetailsCode: "01 03 04*", + recipientCompanySiret: destination.siret } }); @@ -2357,6 +3216,12 @@ describe("Mutation.updateForm", () => { it("should not be possible to update a weight > 40 T when transport mode is ROAD", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const { mutate } = makeClient(user); const form = await formFactory({ ownerId: user.id, @@ -2365,6 +3230,7 @@ describe("Mutation.updateForm", () => { emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX2, wasteDetailsCode: "01 03 04*", + recipientCompanySiret: destination.siret, transporters: { create: { transporterTransportMode: "ROAD", @@ -2394,6 +3260,12 @@ describe("Mutation.updateForm", () => { it("should be possible to update a weight > 40 T when transport mode is not ROAD", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const { mutate } = makeClient(user); const form = await formFactory({ ownerId: user.id, @@ -2403,6 +3275,7 @@ describe("Mutation.updateForm", () => { emitterType: EmitterType.APPENDIX2, wasteDetailsCode: "01 03 04*", wasteDetailsQuantity: 10, + recipientCompanySiret: destination.siret, transporters: { create: { transporterTransportMode: "ROAD", @@ -2438,6 +3311,12 @@ describe("Mutation.updateForm", () => { it("should be possible to update a weight > 40 T when deleting first transporter by road", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const { mutate } = makeClient(user); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const form = await formFactory({ ownerId: user.id, opt: { @@ -2446,6 +3325,7 @@ describe("Mutation.updateForm", () => { emitterType: EmitterType.APPENDIX2, wasteDetailsCode: "01 03 04*", wasteDetailsQuantity: 10, + recipientCompanySiret: destination.siret, transporters: { create: { transporterTransportMode: "ROAD", number: 1 } } @@ -2485,6 +3365,12 @@ describe("Mutation.updateForm", () => { it("should clean appendix1 items on update", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const { company: producerCompany } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const { mutate } = makeClient(user); const appendix1_1 = await prisma.form.create({ @@ -2526,6 +3412,7 @@ describe("Mutation.updateForm", () => { wasteDetailsCode: "16 06 01*", emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX1, + recipientCompanySiret: destination.siret, grouping: { create: { initialFormId: appendix1_1.id, quantity: 0 } } } }); @@ -2555,6 +3442,12 @@ describe("Mutation.updateForm", () => { it("should append appendix1 item on update", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const { company: producerCompany } = await userWithCompanyFactory("MEMBER"); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + const { mutate } = makeClient(user); const appendix1_1 = await prisma.form.create({ @@ -2568,6 +3461,7 @@ describe("Mutation.updateForm", () => { emitterCompanyContact: "Contact", emitterCompanyPhone: "01 01 01 01 01", emitterCompanyMail: "annexe1@test.com", + recipientCompanySiret: destination.siret, wasteDetailsCode: "16 06 01*", owner: { connect: { id: user.id } } } @@ -2596,6 +3490,8 @@ describe("Mutation.updateForm", () => { wasteDetailsCode: "16 06 01*", emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX1, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendix1_1.id, quantity: 0 } } } }); @@ -2646,6 +3542,11 @@ describe("Mutation.updateForm", () => { }); // Group with appendix1_1 + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { @@ -2653,6 +3554,8 @@ describe("Mutation.updateForm", () => { wasteDetailsCode: "16 06 01*", emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX1, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendix1_1.id, quantity: 0 } } } }); @@ -2714,6 +3617,11 @@ describe("Mutation.updateForm", () => { } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Group with appendix1_1 const form = await formFactory({ ownerId: user.id, @@ -2721,7 +3629,8 @@ describe("Mutation.updateForm", () => { status: Status.SEALED, wasteDetailsCode: "16 06 01*", emitterCompanySiret: company.siret, - emitterType: EmitterType.APPENDIX1 + emitterType: EmitterType.APPENDIX1, + recipientCompanySiret: destination.siret } }); @@ -2781,6 +3690,12 @@ describe("Mutation.updateForm", () => { } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); + // Group with appendix1_1 const form = await formFactory({ ownerId: user.id, @@ -2790,7 +3705,8 @@ describe("Mutation.updateForm", () => { emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX1, ecoOrganismeName: ecoOrganisme.company.name, - ecoOrganismeSiret: ecoOrganisme.company.siret + ecoOrganismeSiret: ecoOrganisme.company.siret, + recipientCompanySiret: destination.siret } }); @@ -2816,6 +3732,11 @@ describe("Mutation.updateForm", () => { async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const { mutate } = makeClient(user); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { @@ -2823,6 +3744,8 @@ describe("Mutation.updateForm", () => { status: "SEALED", emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX2, + recipientCompanySiret: destination.siret, + wasteDetailsCode: "01 03 04*", wasteDetailsQuantity: 50, transporters: { @@ -2872,6 +3795,11 @@ describe("Mutation.updateForm", () => { } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Group with appendix1_1 const form = await formFactory({ ownerId: user.id, @@ -2880,6 +3808,8 @@ describe("Mutation.updateForm", () => { wasteDetailsCode: "16 06 01*", emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX1, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendix1_1.id, quantity: 0 } } } }); @@ -2926,7 +3856,11 @@ describe("Mutation.updateForm", () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const { company: producerCompany } = await userWithCompanyFactory("MEMBER"); const { mutate } = makeClient(user); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Group with appendix1_1 const form = await formFactory({ ownerId: user.id, @@ -2934,7 +3868,8 @@ describe("Mutation.updateForm", () => { status: Status.DRAFT, wasteDetailsCode: "16 06 01*", emitterCompanySiret: company.siret, - emitterType: EmitterType.APPENDIX1 + emitterType: EmitterType.APPENDIX1, + recipientCompanySiret: destination.siret } }); @@ -2996,6 +3931,11 @@ describe("Mutation.updateForm", () => { } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Group with appendix1_1 const form = await formFactory({ ownerId: user.id, @@ -3004,6 +3944,8 @@ describe("Mutation.updateForm", () => { wasteDetailsCode: "16 06 01*", emitterCompanySiret: company.siret, emitterType: EmitterType.APPENDIX1, + recipientCompanySiret: destination.siret, + grouping: { create: { initialFormId: appendix1_1.id, quantity: 0 } } } }); @@ -3046,10 +3988,14 @@ describe("Mutation.updateForm", () => { it.each([Status.DRAFT, Status.SEALED, Status.SIGNED_BY_PRODUCER])( "should be possible to update transporter when status is %p", async status => { - const { user, company } = await userWithCompanyFactory("MEMBER"); + const { user, company } = await userWithCompanyFactory("MEMBER", { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const transporter = await companyFactory({ companyTypes: ["TRANSPORTER"] }); + const form = await formFactory({ ownerId: user.id, opt: { @@ -3093,6 +4039,11 @@ describe("Mutation.updateForm", () => { await prisma.transporterReceipt.findUniqueOrThrow({ where: { id: transporter.company.transporterReceiptId! } }); + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: emitter.user.id, opt: { @@ -3103,6 +4054,8 @@ describe("Mutation.updateForm", () => { emitterCompanyContact: emitter.company.contact, emitterCompanyPhone: emitter.company.contactPhone, emitterCompanyMail: emitter.company.contactEmail, + recipientCompanySiret: destination.siret, + transporters: { create: { transporterCompanySiret: transporter.company.siret, @@ -3145,12 +4098,17 @@ describe("Mutation.updateForm", () => { const transporter3 = await userWithCompanyFactory("MEMBER"); const transporter4 = await userWithCompanyFactory("MEMBER"); const transporter5 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: emitter.user.id, opt: { status: Status.DRAFT, - emitterCompanySiret: emitter.company.siret + emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret } }); @@ -3260,12 +4218,17 @@ describe("Mutation.updateForm", () => { const emitter = await userWithCompanyFactory("ADMIN"); const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: emitter.user.id, opt: { status: Status.DRAFT, - emitterCompanySiret: emitter.company.siret + emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret } }); @@ -3326,12 +4289,17 @@ describe("Mutation.updateForm", () => { const emitter = await userWithCompanyFactory("ADMIN"); const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: emitter.user.id, opt: { status: Status.DRAFT, - emitterCompanySiret: emitter.company.siret + emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret } }); @@ -3409,12 +4377,17 @@ describe("Mutation.updateForm", () => { const emitter = await userWithCompanyFactory("ADMIN"); const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: emitter.user.id, opt: { status: Status.DRAFT, - emitterCompanySiret: emitter.company.siret + emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret } }); @@ -3472,12 +4445,17 @@ describe("Mutation.updateForm", () => { const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); const transporter3 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: emitter.user.id, opt: { status: Status.DRAFT, - emitterCompanySiret: emitter.company.siret + emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret } }); @@ -3540,7 +4518,11 @@ describe("Mutation.updateForm", () => { const emitter = await userWithCompanyFactory("ADMIN"); const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Create a form that has already been received const form = await formFactory({ ownerId: emitter.user.id, @@ -3550,6 +4532,8 @@ describe("Mutation.updateForm", () => { takenOverAt: new Date(), receivedAt: new Date(), emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret, + transporters: { create: { transporterCompanySiret: transporter1.company.siret, @@ -3594,7 +4578,11 @@ describe("Mutation.updateForm", () => { const emitter = await userWithCompanyFactory("ADMIN"); const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Create a form that has already been sent const form = await formFactory({ ownerId: emitter.user.id, @@ -3603,6 +4591,8 @@ describe("Mutation.updateForm", () => { emittedAt: new Date(), takenOverAt: new Date(), emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret, + transporters: { create: { transporterCompanySiret: transporter1.company.siret, @@ -3648,7 +4638,11 @@ describe("Mutation.updateForm", () => { const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); const transporter3 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Create a form that has already been signed by the first transporter const form = await formFactory({ ownerId: emitter.user.id, @@ -3657,6 +4651,8 @@ describe("Mutation.updateForm", () => { emittedAt: new Date(), takenOverAt: new Date(), emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret, + transporters: { create: { transporterCompanySiret: transporter1.company.siret, @@ -3709,7 +4705,11 @@ describe("Mutation.updateForm", () => { it("should not be possible to update `transporter` (first transporter) when the form has been sent", async () => { const emitter = await userWithCompanyFactory("ADMIN"); const transporter = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Create a form that has already been sent const form = await formFactory({ ownerId: emitter.user.id, @@ -3718,6 +4718,8 @@ describe("Mutation.updateForm", () => { emittedAt: new Date(), takenOverAt: new Date(), emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret, + transporters: { create: { transporterCompanySiret: transporter.company.siret, @@ -3752,7 +4754,11 @@ describe("Mutation.updateForm", () => { const emitter = await userWithCompanyFactory("ADMIN"); const transporter1 = await userWithCompanyFactory("MEMBER"); const transporter2 = await userWithCompanyFactory("MEMBER"); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); // Create a form that has already been received const form = await formFactory({ ownerId: emitter.user.id, @@ -3761,6 +4767,8 @@ describe("Mutation.updateForm", () => { emittedAt: new Date(), takenOverAt: new Date(), emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret, + transporters: { create: { transporterCompanySiret: transporter1.company.siret, @@ -3864,13 +4872,19 @@ describe("Mutation.updateForm", () => { name: "Nom intermédiaire 2", siret: siretify() }); - + // recipient needs appropriate profiles and subprofiles + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: emitter.user.id, opt: { status: Status.SENT, takenOverAt: new Date(), emitterCompanySiret: emitter.company.siret, + recipientCompanySiret: destination.siret, + transporters: { create: { transporterCompanySiret: transporter1.company.siret, @@ -3940,11 +4954,16 @@ describe("Mutation.updateForm", () => { it("should log in an event the updated data", async () => { const { company, user } = await userWithCompanyFactory(UserRole.ADMIN); + const destination = await companyFactory({ + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + }); const form = await formFactory({ ownerId: user.id, opt: { status: "SEALED", - emitterCompanySiret: company.siret + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret } }); diff --git a/back/src/forms/resolvers/mutations/duplicateForm.ts b/back/src/forms/resolvers/mutations/duplicateForm.ts index df08e1384f..caa5a1d428 100644 --- a/back/src/forms/resolvers/mutations/duplicateForm.ts +++ b/back/src/forms/resolvers/mutations/duplicateForm.ts @@ -297,6 +297,10 @@ const duplicateFormResolver: MutationResolvers["duplicateForm"] = async ( const sirenified = await sirenifyFormCreateInput(newFormInput, []); + // We do not check destination company profiles here, hence duplicating a bsdd created before rules enforcement + // will produce a bsdd whose destination does not match current subprofile requirements. + // The markAsSealed mutation will raise an error and the rule will be enforced then. + const newForm = await formRepository.create(sirenified, { duplicate: { id: existingForm.id } }); diff --git a/back/src/forms/resolvers/queries/__tests__/appendixforms.integration.ts b/back/src/forms/resolvers/queries/__tests__/appendixforms.integration.ts index 4f168bc79f..5d8b199997 100644 --- a/back/src/forms/resolvers/queries/__tests__/appendixforms.integration.ts +++ b/back/src/forms/resolvers/queries/__tests__/appendixforms.integration.ts @@ -11,7 +11,7 @@ import { gql } from "graphql-tag"; import { prisma } from "@td/prisma"; import { updateAppendix2Fn } from "../../../updateAppendix2"; import { AuthType } from "../../../../auth"; - +import { CompanyType, WasteProcessorType } from "@prisma/client"; const APPENDIX_FORMS = gql` query AppendixForm($siret: String!) { appendixForms(siret: $siret) { @@ -33,7 +33,11 @@ describe("Test appendixForms", () => { const { user: emitter, company: emitterCompany } = await userWithCompanyFactory("ADMIN"); const { user: ttr, company: ttrCompany } = await userWithCompanyFactory( - "ADMIN" + "ADMIN", + { + companyTypes: [CompanyType.WASTEPROCESSOR], + wasteProcessorTypes: [WasteProcessorType.DANGEROUS_WASTES_INCINERATION] + } ); const { company: destinationCompany } = await userWithCompanyFactory( "ADMIN" diff --git a/back/src/forms/typeDefs/bsdd.inputs.graphql b/back/src/forms/typeDefs/bsdd.inputs.graphql index 31672aaee8..16de1791b4 100644 --- a/back/src/forms/typeDefs/bsdd.inputs.graphql +++ b/back/src/forms/typeDefs/bsdd.inputs.graphql @@ -511,7 +511,17 @@ input RecipientInput { "Opération d'élimination / valorisation prévue (code D/R)" processingOperation: String - "Établissement de destination" + """ + Établissement de destination + + Des restrictions reltives aux profils et sous-profils de l'établissment s'appliquent en fonction du caractère de dangerosité du déchet concerné. + + Sous-profils requis pour déchet dangereux (code déchet avec * ou marqué comme dangereux (isDanegrous) ou présence de pop) : + collectorTypes : DANGEROUS_WASTES, DEEE_WASTES, OTHER_DANGEROUS_WASTES, wasteProcessorTypes: DANGEROUS_WASTES_INCINERATION, DANGEROUS_WASTES_STORAGE, NON_DANGEROUS_WASTES_STORAGE, OTHER_DANGEROUS_WASTES + + Sous-profils requis pour déchet non dangereux : + collectorTypes : NON_DANGEROUS_WASTES, DEEE_WASTES, OTHER_NON_DANGEROUS_WASTES, wasteProcessorTypes : DANGEROUS_WASTES_INCINERATION, NON_DANGEROUS_WASTES_INCINERATION, CREMATION, NON_DANGEROUS_WASTES_STORAGE, INERT_WASTES_STORAGE, OTHER_NON_DANGEROUS_WASTES + """ company: CompanyInput "Si c'est un entreprosage provisoire ou reconditionnement" diff --git a/back/src/forms/validation.ts b/back/src/forms/validation.ts index 5ddd28f27b..230c04f822 100644 --- a/back/src/forms/validation.ts +++ b/back/src/forms/validation.ts @@ -43,6 +43,7 @@ import { weightConditions, WeightUnits } from "../common/validation"; + import configureYup, { FactorySchemaOf } from "../common/yup/configureYup"; import { CiterneNotWashedOutReason, @@ -620,6 +621,7 @@ const recipientSchemaFn: FactorySchemaOf = ({ .label("Destinataire") .test(siretTests.isRegistered("DESTINATION")) .test(siretTests.isNotDormant) + .test(siretTests.destinationHasAppropriateSubProfiles) .requiredIf(!isDraft, `Destinataire: ${MISSING_COMPANY_SIRET}`), recipientCompanyAddress: yup .string() diff --git a/libs/back/env/src/index.ts b/libs/back/env/src/index.ts index ad78c5c393..fd05674f4e 100644 --- a/libs/back/env/src/index.ts +++ b/libs/back/env/src/index.ts @@ -150,7 +150,12 @@ export const schema = z.object({ GERICO_API_URL: z.string().optional(), GERICO_API_KEY: z.string().optional(), GERICO_WEBHOOK_SLUG: z.string(), - GERICO_WEBHOOK_TOKEN: z.string() + GERICO_WEBHOOK_TOKEN: z.string(), + + VERIFY_DESTINATION_PROFILES_FOR_BSDD_CREATED_AFTER: z + .string() + .datetime() + .optional() }); export const envVariables = schema.superRefine((val, ctx) => {