diff --git a/Changelog.md b/Changelog.md index c1499e108c..96cf2102df 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ et le projet suit un schéma de versionning inspiré de [Calendar Versioning](ht #### :rocket: Nouvelles fonctionnalités - Ajout d'Eco-organisme sur BSVHU [PR 3619](https://github.com/MTES-MCT/trackdechets/pull/3619) +- Ajout des profils Négociant et Courtier sur BSVHU [PR 3645](https://github.com/MTES-MCT/trackdechets/pull/3645) #### :bug: Corrections de bugs diff --git a/back/src/bsda/registry.ts b/back/src/bsda/registry.ts index a9de2afbd3..16b1e26836 100644 --- a/back/src/bsda/registry.ts +++ b/back/src/bsda/registry.ts @@ -417,7 +417,7 @@ export function toIncomingWaste(bsda: RegistryBsda): Required { traderRecepisseNumber: null, brokerCompanyName: bsda.brokerCompanyName, brokerCompanySiret: bsda.brokerCompanySiret, - brokerRecepisseNumber: null, + brokerRecepisseNumber: bsda.brokerRecepisseNumber, emitterCompanyMail: bsda.emitterCompanyMail, ...getOperationData(bsda), nextDestinationProcessingOperation: diff --git a/back/src/bsda/validation/recipify.ts b/back/src/bsda/validation/recipify.ts new file mode 100644 index 0000000000..08082ab696 --- /dev/null +++ b/back/src/bsda/validation/recipify.ts @@ -0,0 +1,72 @@ +import { getTransporterCompanyOrgId } from "@td/constants"; +import { getSealedFields } from "./rules"; +import { ParsedZodBsda } from "./schema"; +import { BsdaValidationContext, ZodBsdaTransformer } from "./types"; +import { CompanyRole } from "../../common/validation/zod/schema"; +import { buildRecipify, RecipifyInputAccessor } from "../../companies/recipify"; + +const recipifyBsdaAccessors = ( + bsd: ParsedZodBsda, + // Tranformations should not be run on sealed fields + sealedFields: string[] +): RecipifyInputAccessor[] => [ + ...(bsd.transporters ?? []).map( + (_, idx) => + ({ + role: CompanyRole.Transporter, + skip: !!bsd.transporters![idx].transporterTransportSignatureDate, + orgIdGetter: () => { + const orgId = getTransporterCompanyOrgId({ + transporterCompanySiret: + bsd.transporters![idx].transporterCompanySiret ?? null, + transporterCompanyVatNumber: + bsd.transporters![idx].transporterCompanyVatNumber ?? null + }); + return orgId ?? null; + }, + setter: async (bsda: ParsedZodBsda, receipt) => { + const transporter = bsda.transporters![idx]; + if (transporter.transporterRecepisseIsExempted) { + transporter.transporterRecepisseNumber = null; + transporter.transporterRecepisseValidityLimit = null; + transporter.transporterRecepisseDepartment = null; + } else { + transporter.transporterRecepisseNumber = + receipt?.receiptNumber ?? null; + transporter.transporterRecepisseValidityLimit = + receipt?.validityLimit ?? null; + transporter.transporterRecepisseDepartment = + receipt?.department ?? null; + } + } + } as RecipifyInputAccessor) + ), + { + role: CompanyRole.Broker, + skip: sealedFields.includes("brokerRecepisseNumber"), + orgIdGetter: () => { + return bsd.brokerCompanySiret ?? null; + }, + setter: async (bsda: ParsedZodBsda, receipt) => { + if (!bsda.brokerRecepisseNumber && receipt?.receiptNumber) { + bsda.brokerRecepisseNumber = receipt.receiptNumber; + } + if (!bsda.brokerRecepisseValidityLimit && receipt?.validityLimit) { + bsda.brokerRecepisseValidityLimit = receipt.validityLimit; + } + if (!bsda.brokerRecepisseDepartment && receipt?.department) { + bsda.brokerRecepisseDepartment = receipt.department; + } + } + } +]; + +export const recipifyBsda: ( + context: BsdaValidationContext +) => ZodBsdaTransformer = context => { + return async bsda => { + const sealedFields = await getSealedFields(bsda, context); + const accessors = recipifyBsdaAccessors(bsda, sealedFields); + return buildRecipify(accessors, bsda); + }; +}; diff --git a/back/src/bsda/validation/rules.ts b/back/src/bsda/validation/rules.ts index 336610be7e..ea32fdbd00 100644 --- a/back/src/bsda/validation/rules.ts +++ b/back/src/bsda/validation/rules.ts @@ -725,8 +725,7 @@ export const bsdaEditionRules: BsdaEditionRules = { sealed: { from: "OPERATION" } }, brokerRecepisseValidityLimit: { - readableFieldName: - "la date de validité de la certification de l'entreprise de travaux", + readableFieldName: "la date de validité du récépissé du courtier", sealed: { from: "OPERATION" } }, wasteCode: { diff --git a/back/src/bsda/validation/schema.ts b/back/src/bsda/validation/schema.ts index ddeb053516..75e4ecd623 100644 --- a/back/src/bsda/validation/schema.ts +++ b/back/src/bsda/validation/schema.ts @@ -35,13 +35,13 @@ import { updateTransporterRecepisse } from "./transformers"; import { sirenifyBsda, sirenifyBsdaTransporter } from "./sirenify"; -import { updateTransportersRecepisse } from "../../common/validation/zod/transformers"; import { CompanyRole, foreignVatNumberSchema, rawTransporterSchema, siretSchema } from "../../common/validation/zod/schema"; +import { recipifyBsda } from "./recipify"; const ZodBsdaPackagingEnum = z.enum([ "BIG_BAG", @@ -292,7 +292,7 @@ export const contextualSchemaAsync = (context: BsdaValidationContext) => { // `enableCompletionTransformers=false`; transformedSyncSchema .transform(sirenifyBsda(context)) - .transform(updateTransportersRecepisse) + .transform(recipifyBsda(context)) .transform(fillWasteConsistenceWhenForwarding) : transformedSyncSchema; diff --git a/back/src/bsds/resolvers/queries/__tests__/bsds.bsvhu.integration.ts b/back/src/bsds/resolvers/queries/__tests__/bsds.bsvhu.integration.ts index 4044789116..bb80c908a0 100644 --- a/back/src/bsds/resolvers/queries/__tests__/bsds.bsvhu.integration.ts +++ b/back/src/bsds/resolvers/queries/__tests__/bsds.bsvhu.integration.ts @@ -92,6 +92,9 @@ describe("Query.bsds.vhus base workflow", () => { destination = await userWithCompanyFactory(UserRole.ADMIN, { companyTypes: { set: ["WASTE_VEHICLES"] + }, + wasteVehiclesTypes: { + set: ["BROYEUR", "DEMOLISSEUR"] } }); }); diff --git a/back/src/bsffs/validation/bsff/recipify.ts b/back/src/bsffs/validation/bsff/recipify.ts new file mode 100644 index 0000000000..0d4708992f --- /dev/null +++ b/back/src/bsffs/validation/bsff/recipify.ts @@ -0,0 +1,54 @@ +import { getTransporterCompanyOrgId } from "@td/constants"; +import { ParsedZodBsff } from "./schema"; +import { BsffValidationContext, ZodBsffTransformer } from "./types"; +import { CompanyRole } from "../../../common/validation/zod/schema"; +import { + buildRecipify, + RecipifyInputAccessor +} from "../../../companies/recipify"; + +const recipifyBsffAccessors = ( + bsd: ParsedZodBsff +): RecipifyInputAccessor[] => [ + ...(bsd.transporters ?? []).map( + (_, idx) => + ({ + role: CompanyRole.Transporter, + skip: !!bsd.transporters![idx].transporterTransportSignatureDate, + orgIdGetter: () => { + const orgId = getTransporterCompanyOrgId({ + transporterCompanySiret: + bsd.transporters![idx].transporterCompanySiret ?? null, + transporterCompanyVatNumber: + bsd.transporters![idx].transporterCompanyVatNumber ?? null + }); + return orgId ?? null; + }, + setter: async (bsff: ParsedZodBsff, receipt) => { + const transporter = bsff.transporters![idx]; + if (transporter.transporterRecepisseIsExempted) { + transporter.transporterRecepisseNumber = null; + transporter.transporterRecepisseValidityLimit = null; + transporter.transporterRecepisseDepartment = null; + } else { + transporter.transporterRecepisseNumber = + receipt?.receiptNumber ?? null; + transporter.transporterRecepisseValidityLimit = + receipt?.validityLimit ?? null; + transporter.transporterRecepisseDepartment = + receipt?.department ?? null; + } + } + } as RecipifyInputAccessor) + ) +]; + +export const recipifyBsff: ( + context: BsffValidationContext +) => ZodBsffTransformer = () => { + return async bsff => { + // const sealedFields = await getSealedFields(bsda, context); + const accessors = recipifyBsffAccessors(bsff); + return buildRecipify(accessors, bsff); + }; +}; diff --git a/back/src/bsffs/validation/bsff/schema.ts b/back/src/bsffs/validation/bsff/schema.ts index c104b0716e..ed17ba1793 100644 --- a/back/src/bsffs/validation/bsff/schema.ts +++ b/back/src/bsffs/validation/bsff/schema.ts @@ -19,12 +19,12 @@ import { checkAndSetPreviousPackagings, updateTransporterRecepisse } from "./transformers"; -import { updateTransportersRecepisse } from "../../../common/validation/zod/transformers"; import { CompanyRole, rawTransporterSchema, siretSchema } from "../../../common/validation/zod/schema"; +import { recipifyBsff } from "./recipify"; export const ZodWasteCodeEnum = z .enum(BSFF_WASTE_CODES, { @@ -195,7 +195,7 @@ export const contextualBsffSchemaAsync = (context: BsffValidationContext) => { return refinedBsffSchema .superRefine(checkCompanies) .transform(sirenifyBsff(context)) - .transform(updateTransportersRecepisse) + .transform(recipifyBsff(context)) .superRefine( // run le check sur les champs requis après les transformations // au cas où des transformations auto-complète certains champs diff --git a/back/src/bsvhu/__tests__/bsvhuEdition.test.ts b/back/src/bsvhu/__tests__/bsvhuEdition.test.ts index 29bc55ac76..2c31cc91fe 100644 --- a/back/src/bsvhu/__tests__/bsvhuEdition.test.ts +++ b/back/src/bsvhu/__tests__/bsvhuEdition.test.ts @@ -1,4 +1,5 @@ import { + BsvhuBrokerInput, BsvhuDestinationInput, BsvhuDestinationType, BsvhuEcoOrganismeInput, @@ -9,6 +10,7 @@ import { BsvhuOperationInput, BsvhuRecepisseInput, BsvhuReceptionInput, + BsvhuTraderInput, BsvhuTransporterInput, BsvhuTransportInput, BsvhuWeightInput, @@ -102,6 +104,16 @@ describe("edition", () => { name: "yyy" }; + const broker: Required = { + company, + recepisse + }; + + const trader: Required = { + company, + recepisse + }; + const input: Required = { emitter, wasteCode: "", @@ -112,7 +124,9 @@ describe("edition", () => { transporter, destination, intermediaries: [company], - ecoOrganisme + ecoOrganisme, + broker, + trader }; const flatInput = graphQlInputToZodBsvhu(input); for (const key of Object.keys(flatInput)) { diff --git a/back/src/bsvhu/__tests__/elastic.integration.ts b/back/src/bsvhu/__tests__/elastic.integration.ts index c622cc5391..8718b065f6 100644 --- a/back/src/bsvhu/__tests__/elastic.integration.ts +++ b/back/src/bsvhu/__tests__/elastic.integration.ts @@ -31,6 +31,8 @@ describe("toBsdElastic > companies Names & OrgIds", () => { let intermediary1: Company; let intermediary2: Company; let ecoOrganisme: Company; + let broker: Company; + let trader: Company; beforeAll(async () => { // Given @@ -45,7 +47,8 @@ describe("toBsdElastic > companies Names & OrgIds", () => { intermediary1 = await companyFactory({ name: "Intermediaire 1" }); intermediary2 = await companyFactory({ name: "Intermediaire 2" }); ecoOrganisme = await companyFactory({ name: "Eco organisme" }); - + broker = await companyFactory({ name: "Broker" }); + trader = await companyFactory({ name: "Trader" }); bsvhu = await bsvhuFactory({ opt: { emitterCompanyName: emitter.name, @@ -57,6 +60,10 @@ describe("toBsdElastic > companies Names & OrgIds", () => { destinationCompanySiret: destination.siret, ecoOrganismeName: ecoOrganisme.name, ecoOrganismeSiret: ecoOrganisme.siret, + brokerCompanySiret: broker.siret, + brokerCompanyName: broker.name, + traderCompanySiret: trader.siret, + traderCompanyName: trader.name, intermediaries: { createMany: { data: [ @@ -80,6 +87,8 @@ describe("toBsdElastic > companies Names & OrgIds", () => { expect(elasticBsvhu.companyNames).toContain(intermediary1.name); expect(elasticBsvhu.companyNames).toContain(intermediary2.name); expect(elasticBsvhu.companyNames).toContain(ecoOrganisme.name); + expect(elasticBsvhu.companyNames).toContain(broker.name); + expect(elasticBsvhu.companyNames).toContain(trader.name); }); test("companyOrgIds > should contain the orgIds of ALL BSVHU companies", async () => { @@ -90,5 +99,7 @@ describe("toBsdElastic > companies Names & OrgIds", () => { expect(elasticBsvhu.companyOrgIds).toContain(intermediary1.siret); expect(elasticBsvhu.companyOrgIds).toContain(intermediary2.siret); expect(elasticBsvhu.companyOrgIds).toContain(ecoOrganisme.siret); + expect(elasticBsvhu.companyOrgIds).toContain(broker.siret); + expect(elasticBsvhu.companyOrgIds).toContain(trader.siret); }); }); diff --git a/back/src/bsvhu/__tests__/factories.vhu.ts b/back/src/bsvhu/__tests__/factories.vhu.ts index c205ef7466..c380d320a1 100644 --- a/back/src/bsvhu/__tests__/factories.vhu.ts +++ b/back/src/bsvhu/__tests__/factories.vhu.ts @@ -22,7 +22,14 @@ export const bsvhuFactory = async ({ companyTypes: ["TRANSPORTER"] }); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] + }); + const brokerCompany = await companyFactory({ + companyTypes: ["BROKER"] + }); + const traderCompany = await companyFactory({ + companyTypes: ["TRADER"] }); const ecoOrganisme = await ecoOrganismeFactory({ handle: { handleBsvhu: true }, @@ -34,6 +41,8 @@ export const bsvhuFactory = async ({ transporterCompanySiret: transporterCompany.siret, destinationCompanySiret: destinationCompany.siret, ecoOrganismeSiret: ecoOrganisme.siret, + brokerCompanySiret: brokerCompany.siret, + traderCompanySiret: traderCompany.siret, ...opt }, include: { @@ -97,7 +106,26 @@ const getVhuFormdata = (): Prisma.BsvhuCreateInput => ({ destinationOperationCode: null, ecoOrganismeSiret: siretify(4), - ecoOrganismeName: "Eco-Organisme" + ecoOrganismeName: "Eco-Organisme", + + brokerCompanyName: "Courtier efficace", + brokerCompanySiret: siretify(5), + brokerCompanyAddress: "15 Rue des Lilas, 33000 Lille", + brokerCompanyContact: "Anton Spencer", + brokerCompanyPhone: "06 67 78 89 91", + brokerCompanyMail: "a.spencer@goodbroker.com", + brokerRecepisseNumber: "receipt number", + brokerRecepisseDepartment: "33", + brokerRecepisseValidityLimit: "2026-11-27T00:00:00.000Z", + traderCompanyName: "Le Négoce QVB", + traderCompanySiret: siretify(6), + traderCompanyAddress: "32 Avenue des Azalées, 33700 Mérignac", + traderCompanyContact: "Benjamin Turner", + traderCompanyPhone: "06 68 35 64 34", + traderCompanyMail: "b.turner@tradingalright.com", + traderRecepisseNumber: "receipt of the firm", + traderRecepisseDepartment: "33", + traderRecepisseValidityLimit: "2026-11-28T00:00:00.000Z" }); export const toIntermediaryCompany = (company: Company, contact = "toto") => ({ diff --git a/back/src/bsvhu/__tests__/registry.integration.ts b/back/src/bsvhu/__tests__/registry.integration.ts index 46aa51a1f3..bf893a3155 100644 --- a/back/src/bsvhu/__tests__/registry.integration.ts +++ b/back/src/bsvhu/__tests__/registry.integration.ts @@ -62,6 +62,27 @@ describe("toGenericWaste", () => { expect(waste.emitterCompanyCity).toBe("Mallemort"); expect(waste.emitterCompanyCountry).toBe("FR"); }); + + it("should contain broker and trader emails", async () => { + // Given + const bsvhu = await bsvhuFactory({ + opt: { + brokerCompanyMail: "broker@mail.com", + traderCompanyMail: "trader@mail.com" + } + }); + + // When + const bsvhuForRegistry = await prisma.bsvhu.findUniqueOrThrow({ + where: { id: bsvhu.id }, + include: RegistryBsvhuInclude + }); + const waste = toGenericWaste(bsvhuForRegistry); + + // Then + expect(waste.brokerCompanyMail).toBe("broker@mail.com"); + expect(waste.traderCompanyMail).toBe("trader@mail.com"); + }); }); describe("toIncomingWaste", () => { @@ -142,6 +163,30 @@ describe("toIncomingWaste", () => { expect(waste.initialEmitterCompanyName).toBeNull(); expect(waste.initialEmitterCompanySiret).toBeNull(); }); + + it("should contain broker and trader information", async () => { + // Given + const bsvhu = await bsvhuFactory({}); + + // When + const bsvhuForRegistry = await prisma.bsvhu.findUniqueOrThrow({ + where: { id: bsvhu.id }, + include: RegistryBsvhuInclude + }); + const waste = toIncomingWaste(bsvhuForRegistry); + + // Then + expect(waste.brokerCompanySiret).toBe(bsvhuForRegistry.brokerCompanySiret); + expect(waste.brokerCompanyName).toBe(bsvhuForRegistry.brokerCompanyName); + expect(waste.brokerRecepisseNumber).toBe( + bsvhuForRegistry.brokerRecepisseNumber + ); + expect(waste.traderCompanySiret).toBe(bsvhuForRegistry.traderCompanySiret); + expect(waste.traderCompanyName).toBe(bsvhuForRegistry.traderCompanyName); + expect(waste.traderRecepisseNumber).toBe( + bsvhuForRegistry.traderRecepisseNumber + ); + }); }); describe("toOutgoingWaste", () => { @@ -190,6 +235,29 @@ describe("toOutgoingWaste", () => { expect(waste.initialEmitterCompanyName).toBeNull(); expect(waste.initialEmitterCompanySiret).toBeNull(); }); + it("should contain broker and trader information", async () => { + // Given + const bsvhu = await bsvhuFactory({}); + + // When + const bsvhuForRegistry = await prisma.bsvhu.findUniqueOrThrow({ + where: { id: bsvhu.id }, + include: RegistryBsvhuInclude + }); + const waste = toOutgoingWaste(bsvhuForRegistry); + + // Then + expect(waste.brokerCompanySiret).toBe(bsvhuForRegistry.brokerCompanySiret); + expect(waste.brokerCompanyName).toBe(bsvhuForRegistry.brokerCompanyName); + expect(waste.brokerRecepisseNumber).toBe( + bsvhuForRegistry.brokerRecepisseNumber + ); + expect(waste.traderCompanySiret).toBe(bsvhuForRegistry.traderCompanySiret); + expect(waste.traderCompanyName).toBe(bsvhuForRegistry.traderCompanyName); + expect(waste.traderRecepisseNumber).toBe( + bsvhuForRegistry.traderRecepisseNumber + ); + }); }); describe("toTransportedWaste", () => { @@ -253,6 +321,29 @@ describe("toTransportedWaste", () => { expect(waste.transporter5CompanySiret).toBeNull(); expect(waste.transporter5NumberPlates).toBeNull(); }); + it("should contain broker and trader information", async () => { + // Given + const bsvhu = await bsvhuFactory({}); + + // When + const bsvhuForRegistry = await prisma.bsvhu.findUniqueOrThrow({ + where: { id: bsvhu.id }, + include: RegistryBsvhuInclude + }); + const waste = toTransportedWaste(bsvhuForRegistry); + + // Then + expect(waste.brokerCompanySiret).toBe(bsvhuForRegistry.brokerCompanySiret); + expect(waste.brokerCompanyName).toBe(bsvhuForRegistry.brokerCompanyName); + expect(waste.brokerRecepisseNumber).toBe( + bsvhuForRegistry.brokerRecepisseNumber + ); + expect(waste.traderCompanySiret).toBe(bsvhuForRegistry.traderCompanySiret); + expect(waste.traderCompanyName).toBe(bsvhuForRegistry.traderCompanyName); + expect(waste.traderRecepisseNumber).toBe( + bsvhuForRegistry.traderRecepisseNumber + ); + }); }); describe("toManagedWaste", () => { @@ -313,6 +404,23 @@ describe("toManagedWaste", () => { expect(waste.transporter5CompanySiret).toBeNull(); expect(waste["transporter5NumberPlates"]).toBeUndefined(); }); + it("should contain broker and trader information", async () => { + // Given + const bsvhu = await bsvhuFactory({}); + + // When + const bsvhuForRegistry = await prisma.bsvhu.findUniqueOrThrow({ + where: { id: bsvhu.id }, + include: RegistryBsvhuInclude + }); + const waste = toManagedWaste(bsvhuForRegistry); + + // Then + expect(waste.brokerCompanySiret).toBe(bsvhuForRegistry.brokerCompanySiret); + expect(waste.brokerCompanyName).toBe(bsvhuForRegistry.brokerCompanyName); + expect(waste.traderCompanySiret).toBe(bsvhuForRegistry.traderCompanySiret); + expect(waste.traderCompanyName).toBe(bsvhuForRegistry.traderCompanyName); + }); }); describe("toAllWaste", () => { @@ -585,6 +693,29 @@ describe("toAllWaste", () => { expect(waste.ecoOrganismeSiren).toBe(ecoOrganisme.siret.substring(0, 9)); expect(waste.ecoOrganismeName).toBe(ecoOrganisme.name); }); + it("should contain broker and trader information", async () => { + // Given + const bsvhu = await bsvhuFactory({}); + + // When + const bsvhuForRegistry = await prisma.bsvhu.findUniqueOrThrow({ + where: { id: bsvhu.id }, + include: RegistryBsvhuInclude + }); + const waste = toAllWaste(bsvhuForRegistry); + + // Then + expect(waste.brokerCompanySiret).toBe(bsvhuForRegistry.brokerCompanySiret); + expect(waste.brokerCompanyName).toBe(bsvhuForRegistry.brokerCompanyName); + expect(waste.brokerRecepisseNumber).toBe( + bsvhuForRegistry.brokerRecepisseNumber + ); + expect(waste.traderCompanySiret).toBe(bsvhuForRegistry.traderCompanySiret); + expect(waste.traderCompanyName).toBe(bsvhuForRegistry.traderCompanyName); + expect(waste.traderRecepisseNumber).toBe( + bsvhuForRegistry.traderRecepisseNumber + ); + }); }); describe("getTransportersData", () => { diff --git a/back/src/bsvhu/__tests__/where.test.ts b/back/src/bsvhu/__tests__/where.test.ts index ff2b4c60d8..d3a6149260 100644 --- a/back/src/bsvhu/__tests__/where.test.ts +++ b/back/src/bsvhu/__tests__/where.test.ts @@ -19,17 +19,21 @@ describe("Bsvhu where builder", () => { it("should convert string filters to db filters", () => { const where: BsvhuWhere = { - emitter: { company: { siret: { _eq: "1234" } } }, - destination: { company: { siret: { _eq: "1234" } } }, - transporter: { company: { siret: { _eq: "1234" } } } + emitter: { company: { siret: { _eq: "emitter" } } }, + destination: { company: { siret: { _eq: "destination" } } }, + transporter: { company: { siret: { _eq: "transporter" } } }, + broker: { company: { siret: { _eq: "broker" } } }, + trader: { company: { siret: { _eq: "trader" } } } }; const dbFilter = toPrismaWhereInput(where); expect(dbFilter).toEqual({ - emitterCompanySiret: { equals: "1234" }, - destinationCompanySiret: { equals: "1234" }, - transporterCompanySiret: { equals: "1234" } + emitterCompanySiret: { equals: "emitter" }, + destinationCompanySiret: { equals: "destination" }, + transporterCompanySiret: { equals: "transporter" }, + brokerCompanySiret: { equals: "broker" }, + traderCompanySiret: { equals: "trader" } }); }); diff --git a/back/src/bsvhu/__tests__/workflow.integration.ts b/back/src/bsvhu/__tests__/workflow.integration.ts index a210aa299a..aab7c67844 100644 --- a/back/src/bsvhu/__tests__/workflow.integration.ts +++ b/back/src/bsvhu/__tests__/workflow.integration.ts @@ -21,7 +21,10 @@ describe("Exemples de circuit du bordereau de suivi de véhicule hors d'usage", const { user: transporterUser, company: transporterCompany } = await userWithCompanyFactory("MEMBER"); const { user: broyeurUser, company: broyeurCompany } = - await userWithCompanyFactory("MEMBER"); + await userWithCompanyFactory("MEMBER", { + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] + }); const producteurToken = await apiKey(producteurUser); const transporteurToken = await apiKey(transporterUser); diff --git a/back/src/bsvhu/converter.ts b/back/src/bsvhu/converter.ts index 38d6b11d2a..9cdf54c67a 100644 --- a/back/src/bsvhu/converter.ts +++ b/back/src/bsvhu/converter.ts @@ -23,7 +23,9 @@ import { BsvhuTransport, BsvhuTransportInput, CompanyInput, - BsvhuEcoOrganisme + BsvhuEcoOrganisme, + BsvhuBroker, + BsvhuTrader } from "../generated/graphql/types"; import { Prisma, @@ -165,6 +167,36 @@ export function expandVhuFormFromDb(form: PrismaVhuForm): GraphqlVhuForm { name: form.ecoOrganismeName, siret: form.ecoOrganismeSiret }), + broker: nullIfNoValues({ + company: nullIfNoValues({ + name: form.brokerCompanyName, + siret: form.brokerCompanySiret, + address: form.brokerCompanyAddress, + contact: form.brokerCompanyContact, + phone: form.brokerCompanyPhone, + mail: form.brokerCompanyMail + }), + recepisse: nullIfNoValues({ + department: form.brokerRecepisseDepartment, + number: form.brokerRecepisseNumber, + validityLimit: processDate(form.brokerRecepisseValidityLimit) + }) + }), + trader: nullIfNoValues({ + company: nullIfNoValues({ + name: form.traderCompanyName, + siret: form.traderCompanySiret, + address: form.traderCompanyAddress, + contact: form.traderCompanyContact, + phone: form.traderCompanyPhone, + mail: form.traderCompanyMail + }), + recepisse: nullIfNoValues({ + department: form.traderRecepisseDepartment, + number: form.traderRecepisseNumber, + validityLimit: processDate(form.traderRecepisseValidityLimit) + }) + }), metadata: null as any }; } @@ -175,6 +207,8 @@ export function flattenVhuInput(formInput: BsvhuInput) { ...flattenVhuDestinationInput(formInput), ...flattenVhuTransporterInput(formInput), ...flattenVhuEcoOrganismeInput(formInput), + ...flattenVhuBrokerInput(formInput), + ...flattenVhuTraderInput(formInput), packaging: chain(formInput, f => f.packaging), wasteCode: chain(formInput, f => f.wasteCode), quantity: chain(formInput, f => f.quantity), @@ -356,6 +390,46 @@ function flattenVhuEcoOrganismeInput({ }; } +function flattenVhuBrokerInput({ broker }: Pick) { + return { + brokerCompanyName: chain(broker, b => chain(b.company, c => c.name)), + brokerCompanySiret: chain(broker, b => chain(b.company, c => c.siret)), + brokerCompanyAddress: chain(broker, b => chain(b.company, c => c.address)), + brokerCompanyContact: chain(broker, b => chain(b.company, c => c.contact)), + brokerCompanyPhone: chain(broker, b => chain(b.company, c => c.phone)), + brokerCompanyMail: chain(broker, b => chain(b.company, c => c.mail)), + brokerRecepisseNumber: chain(broker, b => + chain(b.recepisse, r => r.number) + ), + brokerRecepisseDepartment: chain(broker, b => + chain(b.recepisse, r => r.department) + ), + brokerRecepisseValidityLimit: chain(broker, b => + chain(b.recepisse, r => r.validityLimit) + ) + }; +} + +function flattenVhuTraderInput({ trader }: Pick) { + return { + traderCompanyName: chain(trader, b => chain(b.company, c => c.name)), + traderCompanySiret: chain(trader, b => chain(b.company, c => c.siret)), + traderCompanyAddress: chain(trader, b => chain(b.company, c => c.address)), + traderCompanyContact: chain(trader, b => chain(b.company, c => c.contact)), + traderCompanyPhone: chain(trader, b => chain(b.company, c => c.phone)), + traderCompanyMail: chain(trader, b => chain(b.company, c => c.mail)), + traderRecepisseNumber: chain(trader, b => + chain(b.recepisse, r => r.number) + ), + traderRecepisseDepartment: chain(trader, b => + chain(b.recepisse, r => r.department) + ), + traderRecepisseValidityLimit: chain(trader, b => + chain(b.recepisse, r => r.validityLimit) + ) + }; +} + function flattenTransporterTransportInput( input: | { diff --git a/back/src/bsvhu/elastic.ts b/back/src/bsvhu/elastic.ts index 0ff61c929c..bdfccc3b1b 100644 --- a/back/src/bsvhu/elastic.ts +++ b/back/src/bsvhu/elastic.ts @@ -32,6 +32,8 @@ type ElasticSirets = { ecoOrganismeSiret: string | null | undefined; destinationCompanySiret: string | null | undefined; transporterCompanySiret: string | null | undefined; + brokerCompanySiret: string | null | undefined; + traderCompanySiret: string | null | undefined; intermediarySiret1?: string | null | undefined; intermediarySiret2?: string | null | undefined; intermediarySiret3?: string | null | undefined; @@ -56,6 +58,8 @@ const getBsvhuSirets = (bsvhu: BsvhuForElastic): ElasticSirets => { emitterCompanySiret: bsvhu.emitterCompanySiret, destinationCompanySiret: bsvhu.destinationCompanySiret, ecoOrganismeSiret: bsvhu.ecoOrganismeSiret, + brokerCompanySiret: bsvhu.brokerCompanySiret, + traderCompanySiret: bsvhu.traderCompanySiret, transporterCompanySiret: getTransporterCompanyOrgId(bsvhu), ...intermediarySirets }; @@ -204,13 +208,13 @@ export function toBsdElastic(bsvhu: BsvhuForElastic): BsdElastic { destinationCustomInfo: "", destinationCap: "", - brokerCompanyName: "", - brokerCompanySiret: "", - brokerCompanyAddress: "", + brokerCompanyName: bsvhu.brokerCompanyName ?? "", + brokerCompanySiret: bsvhu.brokerCompanySiret ?? "", + brokerCompanyAddress: bsvhu.brokerCompanyAddress ?? "", - traderCompanyName: "", - traderCompanySiret: "", - traderCompanyAddress: "", + traderCompanyName: bsvhu.traderCompanyName ?? "", + traderCompanySiret: bsvhu.traderCompanySiret ?? "", + traderCompanyAddress: bsvhu.traderCompanyAddress ?? "", ecoOrganismeName: bsvhu.ecoOrganismeName ?? "", ecoOrganismeSiret: bsvhu.ecoOrganismeSiret ?? "", @@ -250,6 +254,8 @@ export function toBsdElastic(bsvhu: BsvhuForElastic): BsdElastic { bsvhu.transporterCompanyName, bsvhu.destinationCompanyName, bsvhu.ecoOrganismeName, + bsvhu.brokerCompanyName, + bsvhu.traderCompanyName, ...bsvhu.intermediaries.map(intermediary => intermediary.name) ] .filter(Boolean) @@ -260,6 +266,8 @@ export function toBsdElastic(bsvhu: BsvhuForElastic): BsdElastic { bsvhu.transporterCompanyVatNumber, bsvhu.destinationCompanySiret, bsvhu.ecoOrganismeSiret, + bsvhu.brokerCompanySiret, + bsvhu.traderCompanySiret, ...bsvhu.intermediaries.map(intermediary => intermediary.siret), ...bsvhu.intermediaries.map(intermediary => intermediary.vatNumber) ].filter(Boolean) diff --git a/back/src/bsvhu/examples/workflows/vhuVersBroyeur.ts b/back/src/bsvhu/examples/workflows/vhuVersBroyeur.ts index 856f15394d..cb476ebefa 100644 --- a/back/src/bsvhu/examples/workflows/vhuVersBroyeur.ts +++ b/back/src/bsvhu/examples/workflows/vhuVersBroyeur.ts @@ -11,7 +11,11 @@ const workflow: Workflow = { companies: [ { name: "producteur", companyTypes: ["PRODUCER"] }, { name: "transporteur", companyTypes: ["TRANSPORTER"] }, - { name: "broyeur", companyTypes: ["WASTE_VEHICLES", "WASTEPROCESSOR"] } + { + name: "broyeur", + companyTypes: ["WASTE_VEHICLES", "WASTEPROCESSOR"], + opt: { wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] } + } ], steps: [ createBsvhu("producteur"), diff --git a/back/src/bsvhu/examples/workflows/vhuVersBroyeurTransporteurEtranger.ts b/back/src/bsvhu/examples/workflows/vhuVersBroyeurTransporteurEtranger.ts index 66e31f41cf..19618a58e6 100644 --- a/back/src/bsvhu/examples/workflows/vhuVersBroyeurTransporteurEtranger.ts +++ b/back/src/bsvhu/examples/workflows/vhuVersBroyeurTransporteurEtranger.ts @@ -18,7 +18,11 @@ const workflow: Workflow = { siret: null } }, - { name: "broyeur", companyTypes: ["WASTE_VEHICLES", "WASTEPROCESSOR"] } + { + name: "broyeur", + companyTypes: ["WASTE_VEHICLES", "WASTEPROCESSOR"], + opt: { wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] } + } ], steps: [ createBsvhu("producteur", fixtures as any), diff --git a/back/src/bsvhu/pdf/components/BsvhuPdf.tsx b/back/src/bsvhu/pdf/components/BsvhuPdf.tsx index de28bfb96a..001e76f3e9 100644 --- a/back/src/bsvhu/pdf/components/BsvhuPdf.tsx +++ b/back/src/bsvhu/pdf/components/BsvhuPdf.tsx @@ -1,5 +1,10 @@ import * as React from "react"; -import { Document, formatDate, SignatureStamp } from "../../../common/pdf"; +import { + Document, + formatDate, + FormCompanyFields, + SignatureStamp +} from "../../../common/pdf"; import { Bsvhu, OperationMode } from "../../../generated/graphql/types"; import { Recepisse } from "./Recepisse"; import { getOperationModeLabel } from "../../../common/operationModes"; @@ -270,7 +275,7 @@ export function BsvhuPdf({ bsvhu, qrCode, renderEmpty }: Props) { {/* End Waste identification*/} - {/* Emitter signature*/}{" "} + {/* Emitter signature*/}

@@ -295,12 +300,80 @@ export function BsvhuPdf({ bsvhu, qrCode, renderEmpty }: Props) {

- {/* End Emitter signature*/} + {/* End Emitter signature */} + {/* Trader informations or broker information if no trader */} +
+
+

+ + 8.{bsvhu.trader && bsvhu.broker ? "1" : ""}{" "} + {" "} + Négociant{" "} + {" "} + Courtier + +

+
+
+ +
+
+ +
+
+
+
+ {/* End Broker/Trader informations */} + {/* Broker information */} + {bsvhu.broker ? ( +
+
+

+ + 8.{bsvhu.trader ? "2" : ""}{" "} + Négociant{" "} + Courtier + +

+
+
+ +
+
+ +
+
+
+
+ ) : null} + {/* End Broker informations */} {/* Transporter */}

- 8. Transporteur + 9. Transporteur

@@ -364,7 +437,7 @@ export function BsvhuPdf({ bsvhu, qrCode, renderEmpty }: Props) {

- 9. Installation de destination + 10. Installation de destination

- 10. Réalisation de l’opération + 11. Réalisation de l’opération
[ i.siret, @@ -59,6 +63,17 @@ function contributors(bsvhu: Bsvhu, input?: BsvhuInput): string[] { updateEcoOrganismeCompanySiret !== undefined ? updateEcoOrganismeCompanySiret : bsvhu.ecoOrganismeSiret; + + const brokerCompanySiret = + updateBrokerCompanySiret !== undefined + ? updateBrokerCompanySiret + : bsvhu.brokerCompanySiret; + + const traderCompanySiret = + updateTraderCompanySiret !== undefined + ? updateTraderCompanySiret + : bsvhu.traderCompanySiret; + const intermediariesOrgIds = input?.intermediaries !== undefined ? updateIntermediaries @@ -70,6 +85,8 @@ function contributors(bsvhu: Bsvhu, input?: BsvhuInput): string[] { transporterCompanySiret, transporterCompanyVatNumber, ecoOrganismeCompanySiret, + brokerCompanySiret, + traderCompanySiret, ...intermediariesOrgIds ].filter(Boolean); } @@ -83,7 +100,9 @@ function creators(input: BsvhuInput) { input.ecoOrganisme?.siret, input.transporter?.company?.siret, input.transporter?.company?.vatNumber, - input.destination?.company?.siret + input.destination?.company?.siret, + input.broker?.company?.siret, + input.trader?.company?.siret ].filter(Boolean); } diff --git a/back/src/bsvhu/registry.ts b/back/src/bsvhu/registry.ts index eb795ee62d..861a775c78 100644 --- a/back/src/bsvhu/registry.ts +++ b/back/src/bsvhu/registry.ts @@ -123,6 +123,14 @@ export function getRegistryFields( registryFields.isOutgoingWasteFor.push(bsvhu.ecoOrganismeSiret); registryFields.isAllWasteFor.push(bsvhu.ecoOrganismeSiret); } + if (bsvhu.brokerCompanySiret) { + registryFields.isManagedWasteFor.push(bsvhu.brokerCompanySiret); + registryFields.isAllWasteFor.push(bsvhu.brokerCompanySiret); + } + if (bsvhu.traderCompanySiret) { + registryFields.isManagedWasteFor.push(bsvhu.traderCompanySiret); + registryFields.isAllWasteFor.push(bsvhu.traderCompanySiret); + } if (bsvhu.intermediaries?.length) { for (const intermediary of bsvhu.intermediaries) { const intermediaryOrgId = getIntermediaryCompanyOrgId(intermediary); @@ -219,6 +227,8 @@ export function toGenericWaste(bsvhu: RegistryBsvhu): GenericWaste { emitterCompanyName: bsvhu.emitterCompanyName, emitterCompanySiret: bsvhu.emitterCompanySiret, emitterCompanyIrregularSituation: !!bsvhu.emitterIrregularSituation, + brokerCompanyMail: bsvhu.brokerCompanyMail, + traderCompanyMail: bsvhu.traderCompanyMail, weight: bsvhu.weightValue ? new Decimal(bsvhu.weightValue) .dividedBy(1000) @@ -237,12 +247,12 @@ export function toIncomingWaste(bsvhu: RegistryBsvhu): Required { ...emptyIncomingWaste, ...genericWaste, destinationReceptionDate: bsvhu.destinationReceptionDate, - traderCompanyName: null, - traderCompanySiret: null, - traderRecepisseNumber: null, - brokerCompanyName: null, - brokerCompanySiret: null, - brokerRecepisseNumber: null, + traderCompanyName: bsvhu.traderCompanyName, + traderCompanySiret: bsvhu.traderCompanySiret, + traderRecepisseNumber: bsvhu.traderRecepisseNumber, + brokerCompanyName: bsvhu.brokerCompanyName, + brokerCompanySiret: bsvhu.brokerCompanySiret, + brokerRecepisseNumber: bsvhu.brokerRecepisseNumber, emitterCompanyMail: bsvhu.emitterCompanyMail, ...getOperationData(bsvhu), ...getTransporterData(bsvhu), @@ -257,13 +267,13 @@ export function toOutgoingWaste(bsvhu: RegistryBsvhu): Required { // Make sure all possible keys are in the exported sheet so that no column is missing ...emptyOutgoingWaste, ...genericWaste, - brokerCompanyName: null, - brokerCompanySiret: null, - brokerRecepisseNumber: null, destinationPlannedOperationMode: null, - traderCompanyName: null, - traderCompanySiret: null, - traderRecepisseNumber: null, + brokerCompanyName: bsvhu.brokerCompanyName, + brokerCompanySiret: bsvhu.brokerCompanySiret, + brokerRecepisseNumber: bsvhu.brokerRecepisseNumber, + traderCompanyName: bsvhu.traderCompanyName, + traderCompanySiret: bsvhu.traderCompanySiret, + traderRecepisseNumber: bsvhu.traderRecepisseNumber, ...getOperationData(bsvhu), weight: bsvhu.weightValue ? bsvhu.weightValue / 1000 : bsvhu.weightValue, ...getOperationData(bsvhu), @@ -283,12 +293,12 @@ export function toTransportedWaste( ...genericWaste, destinationReceptionDate: bsvhu.destinationReceptionDate, weight: bsvhu.weightValue ? bsvhu.weightValue / 1000 : bsvhu.weightValue, - traderCompanyName: null, - traderCompanySiret: null, - traderRecepisseNumber: null, - brokerCompanyName: null, - brokerCompanySiret: null, - brokerRecepisseNumber: null, + traderCompanyName: bsvhu.traderCompanyName, + traderCompanySiret: bsvhu.traderCompanySiret, + traderRecepisseNumber: bsvhu.traderRecepisseNumber, + brokerCompanyName: bsvhu.brokerCompanyName, + brokerCompanySiret: bsvhu.brokerCompanySiret, + brokerRecepisseNumber: bsvhu.brokerRecepisseNumber, emitterCompanyMail: bsvhu.emitterCompanyMail, ...getTransporterData(bsvhu, true) }; @@ -305,10 +315,10 @@ export function toManagedWaste(bsvhu: RegistryBsvhu): Required { // Make sure all possible keys are in the exported sheet so that no column is missing ...emptyManagedWaste, ...genericWaste, - traderCompanyName: null, - traderCompanySiret: null, - brokerCompanyName: null, - brokerCompanySiret: null, + traderCompanyName: bsvhu.traderCompanyName, + traderCompanySiret: bsvhu.traderCompanySiret, + brokerCompanyName: bsvhu.brokerCompanyName, + brokerCompanySiret: bsvhu.brokerCompanySiret, destinationPlannedOperationMode: null, emitterCompanyMail: bsvhu.emitterCompanyMail, ...getTransporterData(bsvhu) @@ -324,14 +334,14 @@ export function toAllWaste(bsvhu: RegistryBsvhu): Required { ...genericWaste, createdAt: bsvhu.createdAt, destinationReceptionDate: bsvhu.destinationReceptionDate, - brokerCompanyName: null, - brokerCompanySiret: null, - brokerRecepisseNumber: null, + brokerCompanyName: bsvhu.brokerCompanyName, + brokerCompanySiret: bsvhu.brokerCompanySiret, + brokerRecepisseNumber: bsvhu.brokerRecepisseNumber, + traderCompanyName: bsvhu.traderCompanyName, + traderCompanySiret: bsvhu.traderCompanySiret, + traderRecepisseNumber: bsvhu.traderRecepisseNumber, destinationPlannedOperationMode: null, weight: bsvhu.weightValue ? bsvhu.weightValue / 1000 : bsvhu.weightValue, - traderCompanyName: null, - traderCompanySiret: null, - traderRecepisseNumber: null, emitterCompanyMail: bsvhu.emitterCompanyMail, ...getOperationData(bsvhu), ...getTransporterData(bsvhu, true), diff --git a/back/src/bsvhu/resolvers/mutations/__tests__/createBsvhu.integration.ts b/back/src/bsvhu/resolvers/mutations/__tests__/createBsvhu.integration.ts index c28fb8d839..8826480b86 100644 --- a/back/src/bsvhu/resolvers/mutations/__tests__/createBsvhu.integration.ts +++ b/back/src/bsvhu/resolvers/mutations/__tests__/createBsvhu.integration.ts @@ -115,7 +115,8 @@ describe("Mutation.Vhu.create", () => { it("should allow creating a valid form for the producer signature", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const input = { @@ -176,7 +177,8 @@ describe("Mutation.Vhu.create", () => { it("should create a bsvhu and autocomplete transporter recepisse", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const transporter = await companyFactory({ @@ -244,7 +246,8 @@ describe("Mutation.Vhu.create", () => { it("should create a bsvhu and ignore recepisse input", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const transporter = await companyFactory({ @@ -319,7 +322,8 @@ describe("Mutation.Vhu.create", () => { it("should create a bsvhu with eco-organisme", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const ecoOrganisme = await ecoOrganismeFactory({ @@ -384,7 +388,8 @@ describe("Mutation.Vhu.create", () => { it("should create a bsvhu with intermediary", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const intermediary = await companyFactory({ @@ -452,7 +457,8 @@ describe("Mutation.Vhu.create", () => { it("should fail if creating a bsvhu with the same intermediary several times", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const intermediary = await companyFactory({ @@ -527,7 +533,8 @@ describe("Mutation.Vhu.create", () => { it("should fail if creating a bsvhu with more than 3 intermediaries", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const intermediary1 = await companyFactory({ @@ -623,7 +630,8 @@ describe("Mutation.Vhu.create", () => { it("should fail if a required field like the recipient agrement is missing", async () => { const { user, company } = await userWithCompanyFactory("MEMBER"); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); const input = { @@ -689,7 +697,8 @@ describe("Mutation.Vhu.create", () => { } ); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); await transporterReceiptFactory({ @@ -757,7 +766,8 @@ describe("Mutation.Vhu.create", () => { } ); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); await transporterReceiptFactory({ @@ -822,7 +832,8 @@ describe("Mutation.Vhu.create", () => { } ); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); await transporterReceiptFactory({ diff --git a/back/src/bsvhu/resolvers/mutations/__tests__/duplicateBsvhu.integration.ts b/back/src/bsvhu/resolvers/mutations/__tests__/duplicateBsvhu.integration.ts index 7065d80925..60d5a1f3de 100644 --- a/back/src/bsvhu/resolvers/mutations/__tests__/duplicateBsvhu.integration.ts +++ b/back/src/bsvhu/resolvers/mutations/__tests__/duplicateBsvhu.integration.ts @@ -79,12 +79,43 @@ describe("mutaion.duplicateBsvhu", () => { await prisma.transporterReceipt.findUniqueOrThrow({ where: { id: transporter.transporterReceiptId! } }); - const destination = await companyFactory(); + const destination = await companyFactory({ + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] + }); const intermediary = await companyFactory(); const ecoOrganisme = await ecoOrganismeFactory({ - handle: { handleBsvhu: true } + handle: { handleBsvhu: true }, + createAssociatedCompany: true + }); + + const broker = await companyFactory({ + companyTypes: ["BROKER"], + brokerReceipt: { + create: { + receiptNumber: "BROKER-RECEIPT-NUMBER", + validityLimit: TODAY.toISOString() as any, + department: "BROKER-RECEIPT-DEPARTMENT" + } + } + }); + const brokerReceipt = await prisma.brokerReceipt.findUniqueOrThrow({ + where: { id: broker.brokerReceiptId! } + }); + const trader = await companyFactory({ + companyTypes: ["TRADER"], + traderReceipt: { + create: { + receiptNumber: "TRADER-RECEIPT-NUMBER", + validityLimit: TODAY.toISOString() as any, + department: "TRADER-RECEIPT-DEPARTMENT" + } + } + }); + const traderReceipt = await prisma.traderReceipt.findUniqueOrThrow({ + where: { id: trader.traderReceiptId! } }); const bsvhu = await bsvhuFactory({ @@ -132,7 +163,25 @@ describe("mutaion.duplicateBsvhu", () => { } }, ecoOrganismeSiret: ecoOrganisme.siret, - ecoOrganismeName: ecoOrganisme.name + ecoOrganismeName: ecoOrganisme.name, + brokerCompanyName: broker.name, + brokerCompanySiret: broker.siret, + brokerCompanyAddress: broker.address, + brokerCompanyContact: broker.contact, + brokerCompanyPhone: broker.contactPhone, + brokerCompanyMail: broker.contactEmail, + brokerRecepisseNumber: brokerReceipt.receiptNumber, + brokerRecepisseDepartment: brokerReceipt.department, + brokerRecepisseValidityLimit: brokerReceipt.validityLimit, + traderCompanyName: trader.name, + traderCompanySiret: trader.siret, + traderCompanyAddress: trader.address, + traderCompanyContact: trader.contact, + traderCompanyPhone: trader.contactPhone, + traderCompanyMail: trader.contactEmail, + traderRecepisseNumber: traderReceipt.receiptNumber, + traderRecepisseDepartment: traderReceipt.department, + traderRecepisseValidityLimit: traderReceipt.validityLimit } }); const { mutate } = makeClient(emitter.user); @@ -204,6 +253,24 @@ describe("mutaion.duplicateBsvhu", () => { transporterRecepisseIsExempted, ecoOrganismeSiret, ecoOrganismeName, + brokerCompanyName, + brokerCompanySiret, + brokerCompanyAddress, + brokerCompanyContact, + brokerCompanyPhone, + brokerCompanyMail, + brokerRecepisseNumber, + brokerRecepisseDepartment, + brokerRecepisseValidityLimit, + traderCompanyName, + traderCompanySiret, + traderCompanyAddress, + traderCompanyContact, + traderCompanyPhone, + traderCompanyMail, + traderRecepisseNumber, + traderRecepisseDepartment, + traderRecepisseValidityLimit, ...rest } = bsvhu; @@ -294,7 +361,25 @@ describe("mutaion.duplicateBsvhu", () => { transporterTransportPlates, transporterRecepisseIsExempted, ecoOrganismeSiret, - ecoOrganismeName + ecoOrganismeName, + brokerCompanyName, + brokerCompanySiret, + brokerCompanyAddress, + brokerCompanyContact, + brokerCompanyPhone, + brokerCompanyMail, + brokerRecepisseNumber, + brokerRecepisseDepartment, + brokerRecepisseValidityLimit, + traderCompanyName, + traderCompanySiret, + traderCompanyAddress, + traderCompanyContact, + traderCompanyPhone, + traderCompanyMail, + traderRecepisseNumber, + traderRecepisseDepartment, + traderRecepisseValidityLimit }); // make sure this test breaks when a new field is added to the Bsvhu model @@ -385,6 +470,8 @@ describe("mutaion.duplicateBsvhu", () => { where: { id: transporterCompany.transporterReceiptId! } }); const destinationCompany = await companyFactory({ + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"], vhuAgrementDemolisseur: { create: { agrementNumber: "UPDATED-AGREEMENT-NUMBER", @@ -569,6 +656,8 @@ describe("mutaion.duplicateBsvhu", () => { where: { id: transporterCompany.transporterReceiptId! } }); const destinationCompany = await companyFactory({ + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"], vhuAgrementDemolisseur: { create: { agrementNumber: "UPDATED-AGREEMENT-NUMBER", diff --git a/back/src/bsvhu/resolvers/mutations/__tests__/updateBsvhu.integration.ts b/back/src/bsvhu/resolvers/mutations/__tests__/updateBsvhu.integration.ts index 489ca915f0..21cb19e811 100644 --- a/back/src/bsvhu/resolvers/mutations/__tests__/updateBsvhu.integration.ts +++ b/back/src/bsvhu/resolvers/mutations/__tests__/updateBsvhu.integration.ts @@ -132,7 +132,6 @@ describe("Mutation.Vhu.update", () => { variables: { id: form.id, input } } ); - expect(data.updateBsvhu.weight!.value).toBe(4); }); diff --git a/back/src/bsvhu/resolvers/mutations/duplicate.ts b/back/src/bsvhu/resolvers/mutations/duplicate.ts index e64fdc6de3..c5906b99f3 100644 --- a/back/src/bsvhu/resolvers/mutations/duplicate.ts +++ b/back/src/bsvhu/resolvers/mutations/duplicate.ts @@ -65,7 +65,8 @@ async function getDuplicateData( user }); - const { emitter, transporter, destination } = await getBsvhuCompanies(bsvhu); + const { emitter, transporter, destination, broker, trader } = + await getBsvhuCompanies(bsvhu); let data: Prisma.BsvhuCreateInput = { ...parsedBsvhu, @@ -90,7 +91,31 @@ async function getDuplicateData( transporter?.contactPhone ?? parsedBsvhu.transporterCompanyPhone, transporterCompanyMail: transporter?.contactEmail ?? parsedBsvhu.transporterCompanyMail, - transporterCompanyVatNumber: parsedBsvhu.transporterCompanyVatNumber + transporterCompanyVatNumber: parsedBsvhu.transporterCompanyVatNumber, + // Broker company info + brokerCompanyMail: broker?.contactEmail ?? bsvhu.brokerCompanyMail, + brokerCompanyPhone: broker?.contactPhone ?? bsvhu.brokerCompanyPhone, + brokerCompanyContact: broker?.contact ?? bsvhu.brokerCompanyContact, + // Broker recepisse + brokerRecepisseNumber: + broker?.brokerReceipt?.receiptNumber ?? bsvhu.brokerRecepisseNumber, + brokerRecepisseValidityLimit: + broker?.brokerReceipt?.validityLimit ?? + bsvhu.brokerRecepisseValidityLimit, + brokerRecepisseDepartment: + broker?.brokerReceipt?.department ?? bsvhu.brokerRecepisseDepartment, + // Trader company info + traderCompanyMail: trader?.contactEmail ?? bsvhu.traderCompanyMail, + traderCompanyPhone: trader?.contactPhone ?? bsvhu.traderCompanyPhone, + traderCompanyContact: trader?.contact ?? bsvhu.traderCompanyContact, + // Trader recepisse + traderRecepisseNumber: + trader?.traderReceipt?.receiptNumber ?? bsvhu.traderRecepisseNumber, + traderRecepisseValidityLimit: + trader?.traderReceipt?.validityLimit ?? + bsvhu.traderRecepisseValidityLimit, + traderRecepisseDepartment: + trader?.traderReceipt?.department ?? bsvhu.traderRecepisseDepartment }; if (intermediaries) { data = { @@ -119,14 +144,18 @@ async function getBsvhuCompanies(bsvhu: PrismaBsvhuForParsing) { bsvhu.emitterCompanySiret, bsvhu.transporterCompanySiret, bsvhu.transporterCompanyVatNumber, - bsvhu.destinationCompanySiret + bsvhu.destinationCompanySiret, + bsvhu.brokerCompanySiret, + bsvhu.traderCompanySiret ].filter(Boolean); // Batch fetch all companies involved in the BSVHU const companies = await prisma.company.findMany({ where: { orgId: { in: companiesOrgIds } }, include: { - transporterReceipt: true + transporterReceipt: true, + brokerReceipt: true, + traderReceipt: true } }); @@ -144,5 +173,13 @@ async function getBsvhuCompanies(bsvhu: PrismaBsvhuForParsing) { company.orgId === bsvhu.transporterCompanyVatNumber ); - return { emitter, destination, transporter }; + const broker = companies.find( + company => company.orgId === bsvhu.brokerCompanySiret + ); + + const trader = companies.find( + company => company.orgId === bsvhu.traderCompanySiret + ); + + return { emitter, destination, transporter, broker, trader }; } diff --git a/back/src/bsvhu/resolvers/queries/__tests__/bsvhu.integration.ts b/back/src/bsvhu/resolvers/queries/__tests__/bsvhu.integration.ts index c3684d4e4f..b212f84e46 100644 --- a/back/src/bsvhu/resolvers/queries/__tests__/bsvhu.integration.ts +++ b/back/src/bsvhu/resolvers/queries/__tests__/bsvhu.integration.ts @@ -44,6 +44,22 @@ query GetBsvhu($id: ID!) { name siret } + broker { + company { + siret + } + recepisse { + number + } + } + trader { + company { + siret + } + recepisse { + number + } + } weight { value } diff --git a/back/src/bsvhu/resolvers/queries/__tests__/bsvhumetadata.integration.ts b/back/src/bsvhu/resolvers/queries/__tests__/bsvhumetadata.integration.ts index e0d2ef78c2..4ea5e52810 100644 --- a/back/src/bsvhu/resolvers/queries/__tests__/bsvhumetadata.integration.ts +++ b/back/src/bsvhu/resolvers/queries/__tests__/bsvhumetadata.integration.ts @@ -161,6 +161,6 @@ describe("Query.Bsvhu", () => { variables: { id: bsd.id } }); - expect(data.bsvhu.metadata?.fields?.sealed?.length).toBe(60); + expect(data.bsvhu.metadata?.fields?.sealed?.length).toBe(78); }); }); diff --git a/back/src/bsvhu/resolvers/queries/__tests__/bsvhus.integration.ts b/back/src/bsvhu/resolvers/queries/__tests__/bsvhus.integration.ts index 765022524b..5734ffd9a9 100644 --- a/back/src/bsvhu/resolvers/queries/__tests__/bsvhus.integration.ts +++ b/back/src/bsvhu/resolvers/queries/__tests__/bsvhus.integration.ts @@ -50,6 +50,22 @@ const GET_BSVHUS = ` number } } + broker { + company { + siret + } + recepisse { + number + } + } + trader { + company { + siret + } + recepisse { + number + } + } weight { value } diff --git a/back/src/bsvhu/resolvers/queries/bsvhus.ts b/back/src/bsvhu/resolvers/queries/bsvhus.ts index debc266ec8..b98a835226 100644 --- a/back/src/bsvhu/resolvers/queries/bsvhus.ts +++ b/back/src/bsvhu/resolvers/queries/bsvhus.ts @@ -27,6 +27,8 @@ export default async function bsvhus( { transporterCompanySiret: { in: orgIdsWithListPermission } }, { transporterCompanyVatNumber: { in: orgIdsWithListPermission } }, { destinationCompanySiret: { in: orgIdsWithListPermission } }, + { brokerCompanySiret: { in: orgIdsWithListPermission } }, + { traderCompanySiret: { in: orgIdsWithListPermission } }, { intermediariesOrgIds: { hasSome: orgIdsWithListPermission } } ] }; diff --git a/back/src/bsvhu/typeDefs/bsvhu.inputs.graphql b/back/src/bsvhu/typeDefs/bsvhu.inputs.graphql index 619723b271..1457a9f1d6 100644 --- a/back/src/bsvhu/typeDefs/bsvhu.inputs.graphql +++ b/back/src/bsvhu/typeDefs/bsvhu.inputs.graphql @@ -16,6 +16,10 @@ input BsvhuWhere { transporter: BsvhuTransporterWhere "Filtre sur le champ destination." destination: BsvhuDestinationWhere + "Filtre sur le champ broker." + broker: BsvhuBrokerWhere + "Filtre sur le champ négociant." + trader: BsvhuTraderWhere "ET logique" _and: [BsvhuWhere!] "OU logique" @@ -59,6 +63,16 @@ input BsvhuDestinationWhere { operation: BsvhuOperationWhere } +"Champs possible pour le filtre sur le courtier." +input BsvhuBrokerWhere { + company: CompanyWhere +} + +"Champs possible pour le filtre sur le négociant." +input BsvhuTraderWhere { + company: CompanyWhere +} + "Champs possibles pour le filtre sur la réception" input BsvhuReceptionWhere { date: DateFilter @@ -89,6 +103,13 @@ input BsvhuInput { destination: BsvhuDestinationInput "Détails sur le transporteur" transporter: BsvhuTransporterInput + + "Courtier" + broker: BsvhuBrokerInput + + "Négociant" + trader: BsvhuTraderInput + """ Liste d'entreprises intermédiaires. Un intermédiaire est une entreprise qui prend part à la gestion du déchet, mais pas à la responsabilité de la traçabilité. Il pourra lire ce bordereau, sans étape de signature. @@ -240,6 +261,20 @@ input BsvhuTransporterInput { transport: BsvhuTransportInput } +input BsvhuBrokerInput { + "Coordonnées de l'entreprise courtier" + company: CompanyInput + "Récépissé courtier" + recepisse: BsvhuRecepisseInput +} + +input BsvhuTraderInput { + "Coordonnées de l'entreprise de négoce" + company: CompanyInput + "Récépissé courtier" + recepisse: BsvhuRecepisseInput +} + input BsvhuTransportInput { "Date de prise en charge" takenOverAt: DateTime diff --git a/back/src/bsvhu/typeDefs/bsvhu.objects.graphql b/back/src/bsvhu/typeDefs/bsvhu.objects.graphql index 3106fb537a..ad5379216c 100644 --- a/back/src/bsvhu/typeDefs/bsvhu.objects.graphql +++ b/back/src/bsvhu/typeDefs/bsvhu.objects.graphql @@ -65,6 +65,12 @@ type Bsvhu { "Eco-organisme" ecoOrganisme: BsvhuEcoOrganisme + "Courtier" + broker: BsvhuBroker + + "Négociant" + trader: BsvhuTrader + metadata: BsvhuMetadata! } @@ -109,11 +115,28 @@ type BsvhuTransport { type BsvhuRecepisse { "Exemption de récépissé" isExempted: Boolean + "Numéro de récépissé" number: String + "Département" department: String + "Date limite de validité" validityLimit: DateTime } +type BsvhuBroker { + "Coordonnées de l'entreprise courtier" + company: FormCompany + "Récépissé courtier" + recepisse: BsvhuRecepisse +} + +type BsvhuTrader { + "Coordonnées de l'entreprise de négoce" + company: FormCompany + "Récépissé négociant" + recepisse: BsvhuRecepisse +} + type BsvhuDestination { "Type de receveur: broyeur ou second centre VHU" type: BsvhuDestinationType diff --git a/back/src/bsvhu/validation/__tests__/validation.integration.ts b/back/src/bsvhu/validation/__tests__/validation.integration.ts index 7cee23b741..1275372a24 100644 --- a/back/src/bsvhu/validation/__tests__/validation.integration.ts +++ b/back/src/bsvhu/validation/__tests__/validation.integration.ts @@ -37,13 +37,16 @@ describe("BSVHU validation", () => { let transporterCompany: Company; let intermediaryCompany: Company; let ecoOrganisme: EcoOrganisme; + let brokerCompany: Company; + let traderCompany: Company; beforeAll(async () => { const emitterCompany = await companyFactory({ companyTypes: ["PRODUCER"] }); transporterCompany = await companyFactory({ companyTypes: ["TRANSPORTER"] }); const destinationCompany = await companyFactory({ - companyTypes: ["WASTE_VEHICLES"] + companyTypes: ["WASTE_VEHICLES"], + wasteVehiclesTypes: ["BROYEUR", "DEMOLISSEUR"] }); foreignTransporter = await companyFactory({ companyTypes: ["TRANSPORTER"], @@ -57,12 +60,20 @@ describe("BSVHU validation", () => { handle: { handleBsvhu: true }, createAssociatedCompany: true }); + brokerCompany = await companyFactory({ + companyTypes: ["BROKER"] + }); + traderCompany = await companyFactory({ + companyTypes: ["TRADER"] + }); const prismaBsvhu = await bsvhuFactory({ opt: { emitterCompanySiret: emitterCompany.siret, transporterCompanySiret: transporterCompany.siret, destinationCompanySiret: destinationCompany.siret, ecoOrganismeSiret: ecoOrganisme.siret, + brokerCompanySiret: brokerCompany.siret, + traderCompanySiret: traderCompany.siret, intermediaries: { create: [toIntermediaryCompany(intermediaryCompany)] } @@ -353,9 +364,7 @@ describe("BSVHU validation", () => { expect((err as ZodError).issues).toEqual([ expect.objectContaining({ message: - `L'installation de destination avec le SIRET \"${company.siret}\" n'est pas inscrite` + - " sur Trackdéchets en tant qu'installation de traitement de VHU. 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" + "Cet établissement n'a pas le profil Installation de traitement de VHU." }) ]); } @@ -608,7 +617,9 @@ describe("BSVHU validation", () => { [bsvhu.transporterCompanySiret!]: searchResult("transporteur"), [bsvhu.destinationCompanySiret!]: searchResult("destinataire"), [intermediaryCompany.siret!]: searchResult("intermédiaire"), - [ecoOrganisme.siret!]: searchResult("ecoOrganisme") + [ecoOrganisme.siret!]: searchResult("ecoOrganisme"), + [brokerCompany.siret!]: searchResult("broker"), + [traderCompany.siret!]: searchResult("trader") }; (searchCompany as jest.Mock).mockImplementation((clue: string) => { return Promise.resolve(searchResults[clue]); @@ -645,6 +656,12 @@ describe("BSVHU validation", () => { expect(sirenified.ecoOrganismeName).toEqual( searchResults[ecoOrganisme.siret!].name ); + expect(sirenified.brokerCompanyName).toEqual( + searchResults[brokerCompany.siret!].name + ); + expect(sirenified.traderCompanyName).toEqual( + searchResults[traderCompany.siret!].name + ); }); it("should not overwrite `name` and `address` based on SIRENE data for sealed fields", async () => { const searchResults = { diff --git a/back/src/bsvhu/validation/helpers.ts b/back/src/bsvhu/validation/helpers.ts index 46ca506bcb..761a3357ce 100644 --- a/back/src/bsvhu/validation/helpers.ts +++ b/back/src/bsvhu/validation/helpers.ts @@ -116,7 +116,13 @@ export async function getBsvhuUserFunctions( orgIds.includes(bsvhu.transporterCompanyVatNumber)), isEcoOrganisme: bsvhu.ecoOrganismeSiret != null && - orgIds.includes(bsvhu.ecoOrganismeSiret) + orgIds.includes(bsvhu.ecoOrganismeSiret), + isBroker: + bsvhu.brokerCompanySiret != null && + orgIds.includes(bsvhu.brokerCompanySiret), + isTrader: + bsvhu.traderCompanySiret != null && + orgIds.includes(bsvhu.traderCompanySiret) }; } diff --git a/back/src/bsvhu/validation/recipify.ts b/back/src/bsvhu/validation/recipify.ts new file mode 100644 index 0000000000..9871e394ff --- /dev/null +++ b/back/src/bsvhu/validation/recipify.ts @@ -0,0 +1,84 @@ +import { getTransporterCompanyOrgId } from "@td/constants"; +import { getSealedFields } from "./rules"; +import { ParsedZodBsvhu } from "./schema"; +import { BsvhuValidationContext, ZodBsvhuTransformer } from "./types"; +import { CompanyRole } from "../../common/validation/zod/schema"; +import { buildRecipify, RecipifyInputAccessor } from "../../companies/recipify"; + +const recipifyBsvhuAccessors = ( + bsd: ParsedZodBsvhu, + // Tranformations should not be run on sealed fields + sealedFields: string[] +): RecipifyInputAccessor[] => [ + { + role: CompanyRole.Transporter, + skip: sealedFields.includes("transporterRecepisseNumber"), + orgIdGetter: () => { + const orgId = getTransporterCompanyOrgId({ + transporterCompanySiret: bsd.transporterCompanySiret ?? null, + transporterCompanyVatNumber: bsd.transporterCompanyVatNumber ?? null + }); + return orgId ?? null; + }, + setter: async (bsvhu: ParsedZodBsvhu, receipt) => { + if (bsvhu.transporterRecepisseIsExempted) { + bsvhu.transporterRecepisseNumber = null; + bsvhu.transporterRecepisseValidityLimit = null; + bsvhu.transporterRecepisseDepartment = null; + } else { + bsvhu.transporterRecepisseNumber = receipt?.receiptNumber ?? null; + bsvhu.transporterRecepisseValidityLimit = + receipt?.validityLimit ?? null; + bsvhu.transporterRecepisseDepartment = receipt?.department ?? null; + } + } + }, + { + role: CompanyRole.Broker, + skip: sealedFields.includes("brokerRecepisseNumber"), + orgIdGetter: () => { + return bsd.brokerCompanySiret ?? null; + }, + setter: async (bsvhu: ParsedZodBsvhu, receipt) => { + // don't overwrite user input because there are still those inputs in BSVHU forms + if (!bsvhu.brokerRecepisseNumber && receipt?.receiptNumber) { + bsvhu.brokerRecepisseNumber = receipt.receiptNumber; + } + if (!bsvhu.brokerRecepisseValidityLimit && receipt?.validityLimit) { + bsvhu.brokerRecepisseValidityLimit = receipt.validityLimit; + } + if (!bsvhu.brokerRecepisseDepartment && receipt?.department) { + bsvhu.brokerRecepisseDepartment = receipt.department; + } + } + }, + { + role: CompanyRole.Trader, + skip: sealedFields.includes("traderRecepisseNumber"), + orgIdGetter: () => { + return bsd.traderCompanySiret ?? null; + }, + setter: async (bsvhu: ParsedZodBsvhu, receipt) => { + // don't overwrite user input because there are still those inputs in BSVHU forms + if (!bsvhu.traderRecepisseNumber && receipt?.receiptNumber) { + bsvhu.traderRecepisseNumber = receipt.receiptNumber; + } + if (!bsvhu.traderRecepisseValidityLimit && receipt?.validityLimit) { + bsvhu.traderRecepisseValidityLimit = receipt.validityLimit; + } + if (!bsvhu.traderRecepisseDepartment && receipt?.department) { + bsvhu.traderRecepisseDepartment = receipt.department; + } + } + } +]; + +export const recipifyBsvhu: ( + context: BsvhuValidationContext +) => ZodBsvhuTransformer = context => { + return async bsvhu => { + const sealedFields = await getSealedFields(bsvhu, context); + const accessors = recipifyBsvhuAccessors(bsvhu, sealedFields); + return buildRecipify(accessors, bsvhu); + }; +}; diff --git a/back/src/bsvhu/validation/refinements.ts b/back/src/bsvhu/validation/refinements.ts index 57b4c9c75c..8603c91bb2 100644 --- a/back/src/bsvhu/validation/refinements.ts +++ b/back/src/bsvhu/validation/refinements.ts @@ -13,10 +13,12 @@ import { capitalize } from "../../common/strings"; import { BsdType, WasteAcceptationStatus } from "@prisma/client"; import { destinationOperationModeRefinement, + isBrokerRefinement, isDestinationRefinement, isEcoOrganismeRefinement, isEmitterNotDormantRefinement, isRegisteredVatNumberRefinement, + isTraderRefinement, isTransporterRefinement } from "../../common/validation/zod/refinement"; import { EditionRule } from "./rules"; @@ -38,7 +40,7 @@ export const checkCompanies: Refinement = async ( await isDestinationRefinement( bsvhu.destinationCompanySiret, zodContext, - "WASTE_VEHICLES" + bsvhu.destinationType ?? "WASTE_VEHICLES" ); await isTransporterRefinement( { @@ -57,6 +59,8 @@ export const checkCompanies: Refinement = async ( BsdType.BSVHU, zodContext ); + await isBrokerRefinement(bsvhu.brokerCompanySiret, zodContext); + await isTraderRefinement(bsvhu.traderCompanySiret, zodContext); }; export const checkWeights: Refinement = ( diff --git a/back/src/bsvhu/validation/rules.ts b/back/src/bsvhu/validation/rules.ts index 16157624e3..1aa4704da8 100644 --- a/back/src/bsvhu/validation/rules.ts +++ b/back/src/bsvhu/validation/rules.ts @@ -525,6 +525,150 @@ export const bsvhuEditionRules: BsvhuEditionRules = { sealed: { from: "OPERATION" }, path: ["ecoOrganisme", "siret"] }, + brokerCompanyName: { + readableFieldName: "le nom du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "company", "name"] + }, + brokerCompanySiret: { + readableFieldName: "le SIRET du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "company", "siret"] + }, + brokerCompanyAddress: { + readableFieldName: "l'adresse du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "company", "address"] + }, + brokerCompanyContact: { + readableFieldName: "le nom de contact du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "company", "contact"] + }, + brokerCompanyPhone: { + readableFieldName: "le téléphone du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "company", "phone"] + }, + brokerCompanyMail: { + readableFieldName: "le mail du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "company", "mail"] + }, + brokerRecepisseNumber: { + readableFieldName: "le numéro de récépissé du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "recepisse", "number"] + }, + brokerRecepisseDepartment: { + readableFieldName: "le département du récépissé du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "recepisse", "department"] + }, + brokerRecepisseValidityLimit: { + readableFieldName: "la date de validité du récépissé du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.brokerCompanySiret + }, + path: ["broker", "recepisse", "validityLimit"] + }, + traderCompanyName: { + readableFieldName: "le nom du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "company", "name"] + }, + traderCompanySiret: { + readableFieldName: "le SIRET du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "company", "siret"] + }, + traderCompanyAddress: { + readableFieldName: "l'adresse du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "company", "address"] + }, + traderCompanyContact: { + readableFieldName: "le nom de contact du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "company", "contact"] + }, + traderCompanyPhone: { + readableFieldName: "le téléphone du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "company", "phone"] + }, + traderCompanyMail: { + readableFieldName: "le mail du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "company", "mail"] + }, + traderRecepisseNumber: { + readableFieldName: "le numéro de récépissé du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "recepisse", "number"] + }, + traderRecepisseDepartment: { + readableFieldName: "le département du récépissé du négociant", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "recepisse", "department"] + }, + traderRecepisseValidityLimit: { + readableFieldName: "la date de validité du récépissé du courtier", + sealed: { + from: "OPERATION", + when: bsvhu => !!bsvhu.traderCompanySiret + }, + path: ["trader", "recepisse", "validityLimit"] + }, intermediaries: { readableFieldName: "les intermédiaires", sealed: { from: "TRANSPORT" }, diff --git a/back/src/bsvhu/validation/schema.ts b/back/src/bsvhu/validation/schema.ts index aa6f80c9f9..3e77a57b20 100644 --- a/back/src/bsvhu/validation/schema.ts +++ b/back/src/bsvhu/validation/schema.ts @@ -13,7 +13,6 @@ import { BsvhuValidationContext } from "./types"; import { weightSchema } from "../../common/validation/weight"; import { WeightUnits } from "../../common/validation"; import { sirenifyBsvhu } from "./sirenify"; -import { recipifyBsdTransporter } from "../../common/validation/zod/transformers"; import { CompanyRole, foreignVatNumberSchema, @@ -32,6 +31,7 @@ import { WasteAcceptationStatus } from "@prisma/client"; import { fillIntermediariesOrgIds } from "./transformers"; +import { recipifyBsvhu } from "./recipify"; export const ZodWasteCodeEnum = z .enum(BSVHU_WASTE_CODES, { @@ -184,6 +184,24 @@ const rawBsvhuSchema = z.object({ .transform(v => Boolean(v)), ecoOrganismeName: z.string().nullish(), ecoOrganismeSiret: siretSchema(CompanyRole.EcoOrganisme).nullish(), + brokerCompanyName: z.string().nullish(), + brokerCompanySiret: siretSchema(CompanyRole.Broker).nullish(), + brokerCompanyAddress: z.string().nullish(), + brokerCompanyContact: z.string().nullish(), + brokerCompanyPhone: z.string().nullish(), + brokerCompanyMail: z.string().nullish(), + brokerRecepisseNumber: z.string().nullish(), + brokerRecepisseDepartment: z.string().nullish(), + brokerRecepisseValidityLimit: z.coerce.date().nullish(), + traderCompanyName: z.string().nullish(), + traderCompanySiret: siretSchema(CompanyRole.Trader).nullish(), + traderCompanyAddress: z.string().nullish(), + traderCompanyContact: z.string().nullish(), + traderCompanyPhone: z.string().nullish(), + traderCompanyMail: z.string().nullish(), + traderRecepisseNumber: z.string().nullish(), + traderRecepisseDepartment: z.string().nullish(), + traderRecepisseValidityLimit: z.coerce.date().nullish(), intermediaries: z .array(intermediarySchema) .nullish() @@ -233,7 +251,7 @@ export const contextualBsvhuSchemaAsync = (context: BsvhuValidationContext) => { return transformedBsvhuSyncSchema .superRefine(checkCompanies) .transform(sirenifyBsvhu(context)) - .transform(recipifyBsdTransporter) + .transform(recipifyBsvhu(context)) .superRefine( // run le check sur les champs requis après les transformations // au cas où des transformations auto-complète certains champs diff --git a/back/src/bsvhu/validation/sirenify.ts b/back/src/bsvhu/validation/sirenify.ts index 06bb4783ed..793faf3543 100644 --- a/back/src/bsvhu/validation/sirenify.ts +++ b/back/src/bsvhu/validation/sirenify.ts @@ -58,6 +58,22 @@ const sirenifyBsvhuAccessors = ( } } }, + { + siret: bsvhu?.brokerCompanySiret, + skip: sealedFields.includes("brokerCompanySiret"), + setter: (input, companyInput) => { + input.brokerCompanyName = companyInput.name; + input.brokerCompanyAddress = companyInput.address; + } + }, + { + siret: bsvhu?.traderCompanySiret, + skip: sealedFields.includes("traderCompanySiret"), + setter: (input, companyInput) => { + input.traderCompanyName = companyInput.name; + input.traderCompanyAddress = companyInput.address; + } + }, ...(bsvhu.intermediaries ?? []).map( (_, idx) => ({ diff --git a/back/src/bsvhu/validation/types.ts b/back/src/bsvhu/validation/types.ts index c26110df02..9217951190 100644 --- a/back/src/bsvhu/validation/types.ts +++ b/back/src/bsvhu/validation/types.ts @@ -8,6 +8,8 @@ export type BsvhuUserFunctions = { isDestination: boolean; isTransporter: boolean; isEcoOrganisme: boolean; + isBroker: boolean; + isTrader: boolean; }; export type BsvhuValidationContext = { diff --git a/back/src/bsvhu/where.ts b/back/src/bsvhu/where.ts index d0dd64f943..810e633c61 100644 --- a/back/src/bsvhu/where.ts +++ b/back/src/bsvhu/where.ts @@ -34,7 +34,9 @@ function toPrismaBsvhuWhereInput(where: BsvhuWhere): Prisma.BsvhuWhereInput { ), destinationOperationSignatureDate: toPrismaDateFilter( where.destination?.operation?.signature?.date - ) + ), + brokerCompanySiret: toPrismaStringFilter(where.broker?.company?.siret), + traderCompanySiret: toPrismaStringFilter(where.trader?.company?.siret) }); } diff --git a/back/src/common/pdf/components/Recepisse.tsx b/back/src/common/pdf/components/Recepisse.tsx index 161ad271e5..14f9f1f5fa 100644 --- a/back/src/common/pdf/components/Recepisse.tsx +++ b/back/src/common/pdf/components/Recepisse.tsx @@ -1,20 +1,23 @@ import * as React from "react"; import { formatDate } from "../../../common/pdf"; -import { - BsdaRecepisse, - BsffTransporterRecepisse -} from "../../../generated/graphql/types"; type Props = { - recepisse: BsdaRecepisse | BsffTransporterRecepisse | null | undefined; + recepisse: + | { + number?: string | number | null; + department?: string | null; + validityLimit?: Date | null; + } + | null + | undefined; }; export function Recepisse({ recepisse }: Readonly) { return (

- Récépissé n° : {recepisse?.number} + Récépissé n° : {recepisse?.number ?? "-"}
- Département : {recepisse?.department} + Département : {recepisse?.department ?? "-"}
Limite de validité : {formatDate(recepisse?.validityLimit)}

diff --git a/back/src/common/validation/zod/refinement.ts b/back/src/common/validation/zod/refinement.ts index 6a7d5abc3c..1b70f93c66 100644 --- a/back/src/common/validation/zod/refinement.ts +++ b/back/src/common/validation/zod/refinement.ts @@ -1,6 +1,10 @@ import { RefinementCtx, z } from "zod"; import { + isBroker, + isBroyeur, isCollector, + isDemolisseur, + isTrader, isTransporter, isWasteCenter, isWasteProcessor, @@ -124,7 +128,11 @@ export const isRegisteredVatNumberRefinement = async ( export async function isDestinationRefinement( siret: string | null | undefined, ctx: RefinementCtx, - role: "DESTINATION" | "WASTE_VEHICLES" = "DESTINATION", + role: + | "DESTINATION" + | "WASTE_VEHICLES" + | "BROYEUR" + | "DEMOLISSEUR" = "DESTINATION", isExemptedFromVerification?: (destination: Company | null) => boolean ) { const company = await refineSiretAndGetCompany( @@ -132,51 +140,66 @@ export async function isDestinationRefinement( ctx, CompanyRole.Destination ); + if (company) { + if ( + role === "WASTE_VEHICLES" || + role === "BROYEUR" || + role === "DEMOLISSEUR" + ) { + if (!isWasteVehicles(company)) { + return ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: pathFromCompanyRole(CompanyRole.Destination), + message: `Cet établissement n'a pas le profil Installation de traitement de VHU.` + }); + } + if (role === "BROYEUR" && !isBroyeur(company)) { + return ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: pathFromCompanyRole(CompanyRole.Destination), + message: `Cet établissement n'a pas le sous-profil Broyeur.` + }); + } + if (role === "DEMOLISSEUR" && !isDemolisseur(company)) { + return ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: pathFromCompanyRole(CompanyRole.Destination), + message: `Cet établissement n'a pas le sous-profil Casse automobile / démolisseur.` + }); + } + } else if ( + !isCollector(company) && + !isWasteProcessor(company) && + !isWasteCenter(company) && + !isWasteVehicles(company) + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: pathFromCompanyRole(CompanyRole.Destination), + message: + `L'installation de destination ou d’entreposage ou de reconditionnement avec le SIRET "${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` + }); + } - if (company && role === "WASTE_VEHICLES" && !isWasteVehicles(company)) { - return ctx.addIssue({ - code: z.ZodIssueCode.custom, - path: pathFromCompanyRole(CompanyRole.Destination), - message: - `L'installation de destination avec le SIRET "${siret}" n'est pas inscrite` + - ` sur Trackdéchets en tant qu'installation de traitement de VHU. 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` - }); - } else if ( - company && - !isCollector(company) && - !isWasteProcessor(company) && - !isWasteCenter(company) && - !isWasteVehicles(company) - ) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - path: pathFromCompanyRole(CompanyRole.Destination), - message: - `L'installation de destination ou d’entreposage ou de reconditionnement avec le SIRET "${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` - }); - } + if ( + VERIFY_COMPANY === "true" && + company.verificationStatus !== CompanyVerificationStatus.VERIFIED + ) { + if (isExemptedFromVerification && isExemptedFromVerification(company)) { + return true; + } - if ( - company && - VERIFY_COMPANY === "true" && - company.verificationStatus !== CompanyVerificationStatus.VERIFIED - ) { - if (isExemptedFromVerification && isExemptedFromVerification(company)) { - return true; + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: pathFromCompanyRole(CompanyRole.Destination), + message: + `Le compte de l'installation de destination ou d’entreposage ou de reconditionnement prévue` + + ` avec le SIRET ${siret} n'a pas encore été vérifié. Cette installation ne peut pas être visée sur le bordereau.` + }); } - - ctx.addIssue({ - code: z.ZodIssueCode.custom, - path: pathFromCompanyRole(CompanyRole.Destination), - message: - `Le compte de l'installation de destination ou d’entreposage ou de reconditionnement prévue` + - ` avec le SIRET ${siret} n'a pas encore été vérifié. Cette installation ne peut pas être visée sur le bordereau.` - }); } } @@ -251,3 +274,41 @@ export async function isEcoOrganismeRefinement( } } } + +export async function isBrokerRefinement( + siret: string | null | undefined, + ctx: RefinementCtx +) { + const company = await refineSiretAndGetCompany( + siret, + ctx, + CompanyRole.Broker + ); + + if (company && !isBroker(company)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: pathFromCompanyRole(CompanyRole.Broker), + message: `Cet établissement n'a pas le profil Courtier.` + }); + } +} + +export async function isTraderRefinement( + siret: string | null | undefined, + ctx: RefinementCtx +) { + const company = await refineSiretAndGetCompany( + siret, + ctx, + CompanyRole.Trader + ); + + if (company && !isTrader(company)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: pathFromCompanyRole(CompanyRole.Trader), + message: `Cet établissement n'a pas le profil Négociant.` + }); + } +} diff --git a/back/src/common/validation/zod/schema.ts b/back/src/common/validation/zod/schema.ts index 35510c29e5..3e151cef64 100644 --- a/back/src/common/validation/zod/schema.ts +++ b/back/src/common/validation/zod/schema.ts @@ -8,6 +8,7 @@ export enum CompanyRole { Destination = "Destination", EcoOrganisme = "Éco-organisme", Broker = "Courtier", + Trader = "Trader", Worker = "Entreprise de travaux", Intermediary = "Intermédiaire", NextDestination = "Éxutoire" @@ -25,6 +26,8 @@ export const pathFromCompanyRole = (companyRole?: CompanyRole): string[] => { return ["ecoOrganisme", "siret"]; case CompanyRole.Broker: return ["broker", "company", "siret"]; + case CompanyRole.Trader: + return ["trader", "company", "siret"]; case CompanyRole.Worker: return ["worker", "company", "siret"]; case CompanyRole.Intermediary: diff --git a/back/src/companies/recipify.ts b/back/src/companies/recipify.ts index 0bf6075b19..526ba62014 100644 --- a/back/src/companies/recipify.ts +++ b/back/src/companies/recipify.ts @@ -11,6 +11,7 @@ import { BsffTransporter } from "@prisma/client"; import { getTransporterCompanyOrgId } from "@td/constants"; +import { CompanyRole } from "../common/validation/zod/schema"; type RecipifyOutput = { number: string | null; @@ -134,3 +135,73 @@ export async function getTransporterReceipt( transporterRecepisseValidityLimit: transporterReceipt?.validityLimit ?? null }; } + +type Receipt = { + receiptNumber: string; + validityLimit: Date; + department: string; +}; + +export type RecipifyInputAccessor = { + role: CompanyRole.Transporter | CompanyRole.Broker | CompanyRole.Trader; + skip: boolean; + orgIdGetter: () => string | null; + setter: (input: T, receipt: Receipt | null) => Promise; +}; + +export const buildRecipify = async ( + accessors: RecipifyInputAccessor[], + bsd: T +): Promise => { + const recipifiedBsd = { ...bsd }; + for (const { role, skip, setter, orgIdGetter } of accessors) { + if (skip) { + continue; + } + let receipt: Receipt | null = null; + const orgId = orgIdGetter(); + if (!orgId) { + continue; + } + if (role === CompanyRole.Transporter) { + try { + receipt = await prisma.company + .findUnique({ + where: { + orgId + } + }) + .transporterReceipt(); + } catch (error) { + // do nothing + } + } else if (role === CompanyRole.Broker) { + try { + receipt = await prisma.company + .findUnique({ + where: { + orgId + } + }) + .brokerReceipt(); + } catch (error) { + // do nothing + } + } else if (role === CompanyRole.Trader) { + try { + receipt = await prisma.company + .findUnique({ + where: { + orgId + } + }) + .traderReceipt(); + } catch (error) { + // do nothing + } + } + setter(recipifiedBsd, receipt); + } + + return recipifiedBsd; +}; diff --git a/back/src/companies/validation.ts b/back/src/companies/validation.ts index 8a7a08c571..8ee1b14551 100644 --- a/back/src/companies/validation.ts +++ b/back/src/companies/validation.ts @@ -1,5 +1,10 @@ import * as yup from "yup"; -import { Company, CompanyType, WasteProcessorType } from "@prisma/client"; +import { + Company, + CompanyType, + WasteProcessorType, + WasteVehiclesType +} from "@prisma/client"; import { cleanClue, isForeignVat, @@ -31,6 +36,14 @@ export function isWasteVehicles(company: Company) { return company.companyTypes.includes(CompanyType.WASTE_VEHICLES); } +export function isBroyeur(company: Company) { + return company.wasteVehiclesTypes.includes(WasteVehiclesType.BROYEUR); +} + +export function isDemolisseur(company: Company) { + return company.wasteVehiclesTypes.includes(WasteVehiclesType.DEMOLISSEUR); +} + export function isTransporter({ companyTypes }: { @@ -53,6 +66,14 @@ export function isWorker(company: Company) { return company.companyTypes.includes(CompanyType.WORKER); } +export function isBroker(company: Company) { + return company.companyTypes.includes(CompanyType.BROKER); +} + +export function isTrader(company: Company) { + return company.companyTypes.includes(CompanyType.TRADER); +} + export function hasCremationProfile(company: Company) { return company.wasteProcessorTypes.includes(WasteProcessorType.CREMATION); } diff --git a/e2e/src/data/company.ts b/e2e/src/data/company.ts index 8487f8364a..63b35da7be 100644 --- a/e2e/src/data/company.ts +++ b/e2e/src/data/company.ts @@ -1,6 +1,6 @@ import { prisma } from "@td/prisma"; import { generateUniqueTestSiret, randomNbrChain } from "back"; -import { CompanyType, Prisma } from "@prisma/client"; +import { CompanyType, Prisma, WasteVehiclesType } from "@prisma/client"; interface VhuAgrement { agrementNumber: string; @@ -162,6 +162,10 @@ export const seedDefaultCompanies = async () => { { name: "I - Broyeur / casse automobile", companyTypes: [CompanyType.WASTE_VEHICLES], + wasteVehiclesTypes: [ + WasteVehiclesType.BROYEUR, + WasteVehiclesType.DEMOLISSEUR + ], contact: "Monsieur Démolisseur et Broyeur", contactPhone: "0458758956", contactEmail: "monsieurbroyeuretdemolisseur@gmail.com" @@ -202,6 +206,7 @@ export const seedDefaultCompanies = async () => { { name: "N - Démolisseur", companyTypes: [CompanyType.WASTE_VEHICLES], + wasteVehiclesTypes: [WasteVehiclesType.DEMOLISSEUR], contact: "Monsieur Démolisseur", contactPhone: "0473625689", contactEmail: "monsieurdemolisseur@gmail.com" @@ -218,6 +223,7 @@ export const seedDefaultCompanies = async () => { { name: "O - Broyeur", companyTypes: [CompanyType.WASTE_VEHICLES], + wasteVehiclesTypes: [WasteVehiclesType.BROYEUR], contact: "Monsieur Broyeur", contactPhone: "0475875695", contactEmail: "monsieurbroyeur@gmail.com" diff --git a/libs/back/prisma/src/migrations/20240926210744_bsvhu_broker_trader/migration.sql b/libs/back/prisma/src/migrations/20240926210744_bsvhu_broker_trader/migration.sql new file mode 100644 index 0000000000..6eddfa6a7d --- /dev/null +++ b/libs/back/prisma/src/migrations/20240926210744_bsvhu_broker_trader/migration.sql @@ -0,0 +1,25 @@ +-- AlterTable +ALTER TABLE "Bsvhu" ADD COLUMN "brokerCompanyAddress" TEXT, +ADD COLUMN "brokerCompanyContact" TEXT, +ADD COLUMN "brokerCompanyMail" TEXT, +ADD COLUMN "brokerCompanyName" TEXT, +ADD COLUMN "brokerCompanyPhone" TEXT, +ADD COLUMN "brokerCompanySiret" TEXT, +ADD COLUMN "brokerRecepisseDepartment" TEXT, +ADD COLUMN "brokerRecepisseNumber" TEXT, +ADD COLUMN "brokerRecepisseValidityLimit" TIMESTAMPTZ(6), +ADD COLUMN "traderCompanyAddress" TEXT, +ADD COLUMN "traderCompanyContact" TEXT, +ADD COLUMN "traderCompanyMail" TEXT, +ADD COLUMN "traderCompanyName" TEXT, +ADD COLUMN "traderCompanyPhone" TEXT, +ADD COLUMN "traderCompanySiret" TEXT, +ADD COLUMN "traderRecepisseDepartment" TEXT, +ADD COLUMN "traderRecepisseNumber" TEXT, +ADD COLUMN "traderRecepisseValidityLimit" TIMESTAMPTZ(6); + +-- CreateIndex +CREATE INDEX "_BsvhuBrokerCompanySiretIdx" ON "Bsvhu"("brokerCompanySiret"); + +-- CreateIndex +CREATE INDEX "_BsvhuTraderCompanySiretIdx" ON "Bsvhu"("traderCompanySiret"); diff --git a/libs/back/prisma/src/schema.prisma b/libs/back/prisma/src/schema.prisma index 89c5e6859a..db0717e0ed 100644 --- a/libs/back/prisma/src/schema.prisma +++ b/libs/back/prisma/src/schema.prisma @@ -1068,6 +1068,26 @@ model Bsvhu { ecoOrganismeName String? ecoOrganismeSiret String? + brokerCompanyName String? + brokerCompanySiret String? + brokerCompanyAddress String? + brokerCompanyContact String? + brokerCompanyPhone String? + brokerCompanyMail String? + brokerRecepisseNumber String? + brokerRecepisseDepartment String? + brokerRecepisseValidityLimit DateTime? @db.Timestamptz(6) + + traderCompanyName String? + traderCompanySiret String? + traderCompanyAddress String? + traderCompanyContact String? + traderCompanyPhone String? + traderCompanyMail String? + traderRecepisseNumber String? + traderRecepisseDepartment String? + traderRecepisseValidityLimit DateTime? @db.Timestamptz(6) + intermediaries IntermediaryBsvhuAssociation[] // Denormalized fields, storing sirets to speed up queries and avoid expensive joins @@ -1080,6 +1100,8 @@ model Bsvhu { @@index([transporterCompanySiret], map: "_BsvhuTransporterCompanySiretIdx") @@index([transporterCompanyVatNumber], map: "_BsvhuTransporterCompanyVatNumberIdx") @@index([ecoOrganismeSiret], map: "_BsvhuEcoOrganismeSiretIdx") + @@index([brokerCompanySiret], map: "_BsvhuBrokerCompanySiretIdx") + @@index([traderCompanySiret], map: "_BsvhuTraderCompanySiretIdx") @@index([intermediariesOrgIds], map: "_BsvhuIntermediariesOrgIdsIdx", type: Gin) @@index([status], map: "_BsvhuStatusIdx") @@index([updatedAt], map: "_BsvhuUpdatedAtIdx")