From d2215de8e5c7b26994a2affc4ac16428d1de2524 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Thu, 27 Aug 2026 13:19:42 +1000 Subject: [PATCH] fix: reviewed --- .../get-envelope-for-recipient-signing.ts | 23 ++++----------- .../server-only/field/get-fields-for-token.ts | 9 ++++-- .../recipient/get-is-recipient-turn.ts | 29 +++++-------------- .../recipient/get-next-pending-recipient.ts | 25 ++++------------ 4 files changed, 23 insertions(+), 63 deletions(-) diff --git a/packages/lib/server-only/envelope/get-envelope-for-recipient-signing.ts b/packages/lib/server-only/envelope/get-envelope-for-recipient-signing.ts index 116238f3b..e4c93d474 100644 --- a/packages/lib/server-only/envelope/get-envelope-for-recipient-signing.ts +++ b/packages/lib/server-only/envelope/get-envelope-for-recipient-signing.ts @@ -5,13 +5,14 @@ import EnvelopeSchema from '@documenso/prisma/generated/zod/modelSchema/Envelope import SignatureSchema from '@documenso/prisma/generated/zod/modelSchema/SignatureSchema'; import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema'; import UserSchema from '@documenso/prisma/generated/zod/modelSchema/UserSchema'; -import { DocumentSigningOrder, DocumentStatus, EnvelopeType, RecipientRole, SigningStatus } from '@prisma/client'; +import { DocumentSigningOrder, DocumentStatus, EnvelopeType, SigningStatus } from '@prisma/client'; import { z } from 'zod'; import { AppError, AppErrorCode } from '../../errors/app-error'; import type { TDocumentAuthMethods } from '../../types/document-auth'; import { ZEnvelopeFieldSchema, ZFieldSchema } from '../../types/field'; import { ZRecipientLiteSchema } from '../../types/recipient'; +import { isRecipientTurnBySigningOrder } from '../../utils/recipient-groups'; import { isRecipientExpired } from '../../utils/recipients'; import { isRecipientAuthorized } from '../document/is-recipient-authorized'; import { getTeamSettings } from '../team/get-team-settings'; @@ -260,23 +261,9 @@ export const getEnvelopeForRecipientSigning = async ({ }, }); - let isRecipientsTurn = true; - - const currentRecipientIndex = envelope.recipients.findIndex((r) => r.token === token); - - if (envelope.documentMeta.signingOrder === DocumentSigningOrder.SEQUENTIAL && currentRecipientIndex !== -1) { - for (let i = 0; i < currentRecipientIndex; i++) { - // CC recipients have no action to take, so they can never block the flow. - if (envelope.recipients[i].role === RecipientRole.CC) { - continue; - } - - if (envelope.recipients[i].signingStatus !== SigningStatus.SIGNED) { - isRecipientsTurn = false; - break; - } - } - } + const isRecipientsTurn = + envelope.documentMeta.signingOrder !== DocumentSigningOrder.SEQUENTIAL || + isRecipientTurnBySigningOrder(envelope.recipients, recipient); const sender = settings.includeSenderDetails ? { diff --git a/packages/lib/server-only/field/get-fields-for-token.ts b/packages/lib/server-only/field/get-fields-for-token.ts index 8e29ae327..eff45783b 100644 --- a/packages/lib/server-only/field/get-fields-for-token.ts +++ b/packages/lib/server-only/field/get-fields-for-token.ts @@ -1,6 +1,8 @@ import { prisma } from '@documenso/prisma'; import { EnvelopeType, FieldType, RecipientRole, SigningStatus } from '@prisma/client'; +import { getLaterSigningStepRecipientsWhereInput } from '../../utils/recipients'; + export type GetFieldsForTokenOptions = { token: string; }; @@ -31,10 +33,11 @@ export const getFieldsForToken = async ({ token }: GetFieldsForTokenOptions) => signingStatus: { not: SigningStatus.SIGNED, }, - signingOrder: { - gte: recipient.signingOrder ?? 0, - }, envelopeId: recipient.envelopeId, + // Assistants can only assist those in strictly later steps — + // never their own group peers, with null orders as the tail + // step. (Own fields are matched by the sibling OR arm.) + AND: [getLaterSigningStepRecipientsWhereInput(recipient)], }, envelope: { id: recipient.envelopeId, diff --git a/packages/lib/server-only/recipient/get-is-recipient-turn.ts b/packages/lib/server-only/recipient/get-is-recipient-turn.ts index fae6e130b..cbbd8020c 100644 --- a/packages/lib/server-only/recipient/get-is-recipient-turn.ts +++ b/packages/lib/server-only/recipient/get-is-recipient-turn.ts @@ -1,5 +1,7 @@ import { prisma } from '@documenso/prisma'; -import { DocumentSigningOrder, EnvelopeType, RecipientRole, SigningStatus } from '@prisma/client'; +import { DocumentSigningOrder, EnvelopeType } from '@prisma/client'; + +import { isRecipientTurnBySigningOrder } from '../../utils/recipient-groups'; export type GetIsRecipientTurnOptions = { token: string; @@ -17,11 +19,7 @@ export async function getIsRecipientsTurnToSign({ token }: GetIsRecipientTurnOpt }, include: { documentMeta: true, - recipients: { - orderBy: { - signingOrder: 'asc', - }, - }, + recipients: true, }, }); @@ -29,24 +27,11 @@ export async function getIsRecipientsTurnToSign({ token }: GetIsRecipientTurnOpt return true; } - const { recipients } = envelope; + const currentRecipient = envelope.recipients.find((recipient) => recipient.token === token); - const currentRecipientIndex = recipients.findIndex((r) => r.token === token); - - if (currentRecipientIndex === -1) { + if (!currentRecipient) { return false; } - for (let i = 0; i < currentRecipientIndex; i++) { - // CC recipients have no action to take, so they can never block the flow. - if (recipients[i].role === RecipientRole.CC) { - continue; - } - - if (recipients[i].signingStatus !== SigningStatus.SIGNED) { - return false; - } - } - - return true; + return isRecipientTurnBySigningOrder(envelope.recipients, currentRecipient); } diff --git a/packages/lib/server-only/recipient/get-next-pending-recipient.ts b/packages/lib/server-only/recipient/get-next-pending-recipient.ts index 30e867691..4bbd365f9 100644 --- a/packages/lib/server-only/recipient/get-next-pending-recipient.ts +++ b/packages/lib/server-only/recipient/get-next-pending-recipient.ts @@ -1,7 +1,8 @@ import { prisma } from '@documenso/prisma'; -import { EnvelopeType, RecipientRole } from '@prisma/client'; +import { EnvelopeType } from '@prisma/client'; import { mapDocumentIdToSecondaryId } from '../../utils/envelope'; +import { getNextDictatableRecipient } from '../../utils/recipient-groups'; export const getNextPendingRecipient = async ({ documentId, @@ -16,33 +17,17 @@ export const getNextPendingRecipient = async ({ type: EnvelopeType.DOCUMENT, secondaryId: mapDocumentIdToSecondaryId(documentId), }, - // CC recipients are informational only and never take part in signing, - // so they must never be offered as the next pending recipient. - role: { - not: RecipientRole.CC, - }, }, - orderBy: [ - { - signingOrder: { - sort: 'asc', - nulls: 'last', - }, - }, - { - id: 'asc', - }, - ], }); - const currentIndex = recipients.findIndex((r) => r.id === currentRecipientId); + const nextRecipient = getNextDictatableRecipient({ recipients, currentRecipientId }); - if (currentIndex === -1 || currentIndex === recipients.length - 1) { + if (!nextRecipient) { return null; } return { - ...recipients[currentIndex + 1], + ...nextRecipient, token: '', }; };