diff --git a/backend/src/routes/trees.ts b/backend/src/routes/trees.ts index 2be93a0..ac43ec7 100644 --- a/backend/src/routes/trees.ts +++ b/backend/src/routes/trees.ts @@ -2023,7 +2023,7 @@ export const treeRoutes: FastifyPluginAsync = async (app) => { const links = await app.prisma.parentChildLink.findMany({ where: { id: { in: linkIds }, treeId: params.data.id, deletedAt: null }, - select: { id: true, parentPersonId: true, childPersonId: true }, + select: { id: true, parentPersonId: true, childPersonId: true, viaUnionId: true }, }) const linkById = new Map(links.map((link) => [link.id, link])) if (links.length !== linkIds.length) { @@ -2048,13 +2048,21 @@ export const treeRoutes: FastifyPluginAsync = async (app) => { return reply.code(400).send({ error: 'not_siblings' }) } + // L'arbre ne montre qu'un lien par enfant et par union : le lien vers l'autre parent de la même union + // prend le même ordre, pour que les deux restent d'accord await app.prisma.$transaction( - payload.data.order.map((entry, index) => - app.prisma.parentChildLink.updateMany({ - where: { id: { in: entry.linkIds }, treeId: params.data.id, deletedAt: null }, + payload.data.order.map((entry, index) => { + const unionIds = [...new Set(entry.linkIds.map((id) => linkById.get(id)?.viaUnionId).filter((id): id is string => Boolean(id)))] + return app.prisma.parentChildLink.updateMany({ + where: { + treeId: params.data.id, + deletedAt: null, + childPersonId: entry.childId, + OR: [{ id: { in: entry.linkIds } }, ...(unionIds.length ? [{ viaUnionId: { in: unionIds } }] : [])], + }, data: { displayOrder: index + 1 }, - }), - ), + }) + }), ) await app.prisma.auditLog.create({ diff --git a/backend/test/api-security.integration.test.ts b/backend/test/api-security.integration.test.ts index d23082f..39c3cd3 100644 --- a/backend/test/api-security.integration.test.ts +++ b/backend/test/api-security.integration.test.ts @@ -516,10 +516,10 @@ describe('API security and authz', () => { describe('PUT /trees/:id/links/order', () => { const siblingLinks = [ - { id: 'link-a1', parentPersonId: 'father', childPersonId: 'anne' }, - { id: 'link-a2', parentPersonId: 'mother', childPersonId: 'anne' }, - { id: 'link-h1', parentPersonId: 'father', childPersonId: 'hugo' }, - { id: 'link-h2', parentPersonId: 'mother', childPersonId: 'hugo' }, + { id: 'link-a1', parentPersonId: 'father', childPersonId: 'anne', viaUnionId: 'union-1' }, + { id: 'link-a2', parentPersonId: 'mother', childPersonId: 'anne', viaUnionId: 'union-1' }, + { id: 'link-h1', parentPersonId: 'father', childPersonId: 'hugo', viaUnionId: 'union-1' }, + { id: 'link-h2', parentPersonId: 'mother', childPersonId: 'hugo', viaUnionId: 'union-1' }, ] const order = [ { childId: 'hugo', linkIds: ['link-h1', 'link-h2'] }, @@ -542,11 +542,17 @@ describe('API security and authz', () => { expect(response.statusCode).toBe(200) expect(prismaMock.$transaction).toHaveBeenCalledTimes(1) expect(prismaMock.parentChildLink.updateMany).toHaveBeenNthCalledWith(1, { - where: { id: { in: ['link-h1', 'link-h2'] }, treeId: 'tree-1', deletedAt: null }, + where: { + treeId: 'tree-1', deletedAt: null, childPersonId: 'hugo', + OR: [{ id: { in: ['link-h1', 'link-h2'] } }, { viaUnionId: { in: ['union-1'] } }], + }, data: { displayOrder: 1 }, }) expect(prismaMock.parentChildLink.updateMany).toHaveBeenNthCalledWith(2, { - where: { id: { in: ['link-a1', 'link-a2'] }, treeId: 'tree-1', deletedAt: null }, + where: { + treeId: 'tree-1', deletedAt: null, childPersonId: 'anne', + OR: [{ id: { in: ['link-a1', 'link-a2'] } }, { viaUnionId: { in: ['union-1'] } }], + }, data: { displayOrder: 2 }, }) expect(prismaMock.auditLog.create).toHaveBeenCalledWith(expect.objectContaining({