diff --git a/back/src/forms/repository/form/updateAppendix2Forms.ts b/back/src/forms/repository/form/updateAppendix2Forms.ts index 6ddd3e6cc9..b93a14cda9 100644 --- a/back/src/forms/repository/form/updateAppendix2Forms.ts +++ b/back/src/forms/repository/form/updateAppendix2Forms.ts @@ -11,6 +11,8 @@ type FormForUpdateAppendix2Forms = Form & FormWithForwardedIn; export const FormForUpdateAppendix2FormsInclude = FormWithForwardedInInclude; +const DECIMAL_WEIGHT_PRECISION = 6; // gramme + export type UpdateAppendix2Forms = ( forms: FormForUpdateAppendix2Forms[] ) => Promise; @@ -46,7 +48,7 @@ const buildUpdateAppendix2Forms: ( .filter(grp => grp.initialFormId === form.id) .map(grp => grp.quantity) .reduce((prev, cur) => prev + cur, 0) ?? 0 - ).toDecimalPlaces(6); // set precision to gramme + ).toDecimalPlaces(DECIMAL_WEIGHT_PRECISION); // set precision to gramme quantitGroupedByFormId[form.id] = quantityGrouped.toNumber(); @@ -54,9 +56,12 @@ const buildUpdateAppendix2Forms: ( .filter(grp => grp.initialFormId === form.id) .map(g => g.nextForm); + // on a quelques quantityReceived avec des décimales au delà du gramme const groupedInTotality = quantityReceived && - quantityGrouped.greaterThanOrEqualTo(quantityReceived); // case > should not happen + quantityGrouped.greaterThanOrEqualTo( + new Decimal(quantityReceived).toDecimalPlaces(DECIMAL_WEIGHT_PRECISION) // limit precision to circumvent rogue decimal digits + ); // case > should not happen const allSealed = groupementForms.length && diff --git a/back/src/forms/resolvers/mutations/__tests__/markAsProcessed.integration.ts b/back/src/forms/resolvers/mutations/__tests__/markAsProcessed.integration.ts index 056668bc98..3c2660c620 100644 --- a/back/src/forms/resolvers/mutations/__tests__/markAsProcessed.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/markAsProcessed.integration.ts @@ -869,28 +869,44 @@ describe("mutation.markAsProcessed", () => { }); expect(updatedGroupedForm2.status).toEqual("PROCESSED"); }); - - it("should not mark appendix2 forms as processed if they are partially grouped", async () => { + it("should mark appendix2 forms as processed despite rogue decimal digits", async () => { const { user, company } = await userWithCompanyFactory("ADMIN"); - const appendix2 = await formFactory({ + const groupedForm1 = await formFactory({ ownerId: user.id, opt: { - status: "AWAITING_GROUP", - quantityReceived: 1 + status: "GROUPED", + quantityReceived: 1.000000000000001 // decimal digits after 6th place + } + }); + + // it should also work for BSD with temporary storage + const groupedForm2 = await formWithTempStorageFactory({ + ownerId: user.id, + opt: { + status: "GROUPED", + quantityReceived: 0.02 } }); + const form = await formFactory({ ownerId: user.id, opt: { status: "ACCEPTED", + emitterType: "APPENDIX2", recipientCompanyName: company.name, recipientCompanySiret: company.siret, grouping: { - create: { - initialFormId: appendix2.id, - quantity: 0.1 - } + create: [ + { + initialFormId: groupedForm1.id, + quantity: 1 + }, + { + initialFormId: groupedForm2.id, + quantity: groupedForm2.forwardedIn!.quantityReceived!.toNumber() + } + ] } } }); @@ -910,12 +926,66 @@ describe("mutation.markAsProcessed", () => { } }); - const appendix2grouped = await prisma.form.findUniqueOrThrow({ - where: { id: appendix2.id } + const updatedGroupedForm1 = await prisma.form.findUniqueOrThrow({ + where: { id: groupedForm1.id } }); - expect(appendix2grouped.status).toEqual("AWAITING_GROUP"); + expect(updatedGroupedForm1.status).toEqual("PROCESSED"); + + const updatedGroupedForm2 = await prisma.form.findUniqueOrThrow({ + where: { id: groupedForm2.id } + }); + expect(updatedGroupedForm2.status).toEqual("PROCESSED"); }); + it.each([0.1, 1])( + "should not mark appendix2 forms as processed if they are partially grouped - quantity grouped: %p", + async quantityGrouped => { + const { user, company } = await userWithCompanyFactory("ADMIN"); + + const appendix2 = await formFactory({ + ownerId: user.id, + opt: { + status: "AWAITING_GROUP", + quantityReceived: 1.0000001 + } + }); + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "ACCEPTED", + recipientCompanyName: company.name, + recipientCompanySiret: company.siret, + grouping: { + create: { + initialFormId: appendix2.id, + quantity: quantityGrouped + } + } + } + }); + + const { mutate } = makeClient(user); + + await mutate(MARK_AS_PROCESSED, { + variables: { + id: form.id, + processedInfo: { + processingOperationDescription: "Une description", + processingOperationDone: "D 1", + destinationOperationMode: OperationMode.ELIMINATION, + processedBy: "A simple bot", + processedAt: "2018-12-11T00:00:00.000Z" + } + } + }); + + const appendix2grouped = await prisma.form.findUniqueOrThrow({ + where: { id: appendix2.id } + }); + expect(appendix2grouped.status).toEqual("AWAITING_GROUP"); + } + ); + test.each(allowedFormats)("%p is a valid format for processedAt", async f => { const { user, company } = await userWithCompanyFactory("ADMIN"); const form = await formFactory({ diff --git a/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts b/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts index 59be553e8c..c5dc903292 100644 --- a/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts +++ b/back/src/forms/resolvers/mutations/__tests__/markAsSealed.integration.ts @@ -727,6 +727,101 @@ describe("Mutation.markAsSealed", () => { expect(updatedGroupedForm2.status).toEqual("GROUPED"); }); + it("should mark appendix2 forms as grouped despite rogue decimal digits", async () => { + const { user, company } = await userWithCompanyFactory("MEMBER"); + const destination = await destinationFactory(); + const groupedForm1 = await formFactory({ + ownerId: user.id, + opt: { status: "AWAITING_GROUP", quantityReceived: 1.000000000000001 } + }); + + const groupedForm2 = await formWithTempStorageFactory({ + ownerId: user.id, + opt: { + status: "GROUPED", + quantityReceived: 0.02 + } + }); + + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterType: "APPENDIX2", + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, + grouping: { + create: [ + { + initialFormId: groupedForm1.id, + quantity: 1 + }, + { + initialFormId: groupedForm2.id, + quantity: groupedForm2.forwardedIn!.quantityReceived! + } + ] + } + } + }); + + const { mutate } = makeClient(user); + + await mutate(MARK_AS_SEALED, { + variables: { id: form.id } + }); + + const updatedGroupedForm1 = await prisma.form.findUniqueOrThrow({ + where: { id: groupedForm1.id } + }); + expect(updatedGroupedForm1.status).toEqual("GROUPED"); + const updatedGroupedForm2 = await prisma.form.findUniqueOrThrow({ + where: { id: groupedForm2.id } + }); + expect(updatedGroupedForm2.status).toEqual("GROUPED"); + }); + + it.each([0.1, 1])( + "should not mark appendix2 forms as grouped if they are partially grouped - quantity grouped: %p", + async () => { + const { user, company } = await userWithCompanyFactory("MEMBER"); + const destination = await destinationFactory(); + const groupedForm1 = await formFactory({ + ownerId: user.id, + opt: { status: "AWAITING_GROUP", quantityReceived: 1.000001 } + }); + + const form = await formFactory({ + ownerId: user.id, + opt: { + status: "DRAFT", + emitterType: "APPENDIX2", + emitterCompanySiret: company.siret, + recipientCompanySiret: destination.siret, + grouping: { + create: [ + { + initialFormId: groupedForm1.id, + quantity: 0.2 + } + ] + } + } + }); + + const { mutate } = makeClient(user); + + await mutate(MARK_AS_SEALED, { + variables: { id: form.id } + }); + + const updatedGroupedForm1 = await prisma.form.findUniqueOrThrow({ + where: { id: groupedForm1.id } + }); + expect(updatedGroupedForm1.status).toEqual("AWAITING_GROUP"); + } + ); + it("should throw an error if destination is not registered in TD", async () => { const { user, company: emitterCompany } = await userWithCompanyFactory( "MEMBER"