mirror of
https://github.com/documenso/documenso.git
synced 2026-08-26 00:02:43 +10:00
fix: include assistant fields in pending PDFs
This commit is contained in:
@@ -11,6 +11,10 @@ import {
|
||||
DocumentStatus,
|
||||
type EnvelopeType,
|
||||
EnvelopeType as EnvelopeTypeEnum,
|
||||
FieldType,
|
||||
type Prisma,
|
||||
type Recipient,
|
||||
RecipientRole,
|
||||
SigningStatus,
|
||||
type TemplateType,
|
||||
TemplateType as TemplateTypeEnum,
|
||||
@@ -56,6 +60,8 @@ type EnvelopeForPendingDownload = {
|
||||
internalVersion: number;
|
||||
};
|
||||
|
||||
type PendingDownloadRecipient = Pick<Recipient, 'role' | 'signingOrder'>;
|
||||
|
||||
/**
|
||||
* Options shape varies by `version`:
|
||||
* - `signed` / `original`: serves stored bytes; only needs envelope `status` for cache headers.
|
||||
@@ -78,11 +84,11 @@ type HandleEnvelopeItemFileRequestOptions = {
|
||||
envelope: EnvelopeForPendingDownload;
|
||||
|
||||
/**
|
||||
* When set, only fields from recipients who have signed, plus the fields of
|
||||
* the recipient owning this token, are burned in. Keeps recipient downloads
|
||||
* in parity with what the signing page shows them.
|
||||
* Limits the PDF to fields visible to this recipient. Assistants can also
|
||||
* see non-signature fields for unsigned recipients after them.
|
||||
*/
|
||||
recipientToken?: string;
|
||||
recipient?: PendingDownloadRecipient;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -168,6 +174,7 @@ const handlePendingFileRequest = async ({
|
||||
envelope,
|
||||
documentData,
|
||||
recipientToken,
|
||||
recipient,
|
||||
context: c,
|
||||
}: PendingFileRequestOptions) => {
|
||||
if (envelope.status !== DocumentStatus.PENDING) {
|
||||
@@ -190,17 +197,44 @@ const handlePendingFileRequest = async ({
|
||||
});
|
||||
}
|
||||
|
||||
let visibleFieldWhere: Prisma.FieldWhereInput = {};
|
||||
|
||||
if (recipient?.role === RecipientRole.ASSISTANT) {
|
||||
visibleFieldWhere = {
|
||||
OR: [
|
||||
{
|
||||
recipient: {
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: {
|
||||
not: FieldType.SIGNATURE,
|
||||
},
|
||||
recipient: {
|
||||
signingStatus: {
|
||||
not: SigningStatus.SIGNED,
|
||||
},
|
||||
signingOrder: {
|
||||
gt: recipient.signingOrder ?? 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
} else if (recipientToken) {
|
||||
visibleFieldWhere = {
|
||||
recipient: {
|
||||
OR: [{ signingStatus: SigningStatus.SIGNED }, { token: recipientToken }],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const fields = await prisma.field.findMany({
|
||||
where: {
|
||||
envelopeItemId,
|
||||
inserted: true,
|
||||
...(recipientToken
|
||||
? {
|
||||
recipient: {
|
||||
OR: [{ signingStatus: SigningStatus.SIGNED }, { token: recipientToken }],
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...visibleFieldWhere,
|
||||
},
|
||||
include: {
|
||||
signature: true,
|
||||
|
||||
@@ -311,7 +311,20 @@ export const filesRoute = new Hono<HonoEnv>()
|
||||
const envelopeItem = await prisma.envelopeItem.findUnique({
|
||||
where: envelopeWhereQuery,
|
||||
include: {
|
||||
envelope: true,
|
||||
envelope: {
|
||||
include: {
|
||||
recipients: {
|
||||
where: {
|
||||
token,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
signingOrder: true,
|
||||
token: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
documentData: true,
|
||||
},
|
||||
});
|
||||
@@ -338,6 +351,7 @@ export const filesRoute = new Hono<HonoEnv>()
|
||||
envelopeItemId: envelopeItem.id,
|
||||
envelope: envelopeItem.envelope,
|
||||
recipientToken: token,
|
||||
recipient: envelopeItem.envelope.recipients[0],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,80 @@ test.describe('API V2 partial signed PDF downloads', () => {
|
||||
expect(afterCompletionResponse.headers().etag).toBe(recipientOneResponse.headers().etag);
|
||||
});
|
||||
|
||||
test('includes fields filled by an assistant in their partial PDF', async ({ request }) => {
|
||||
const { envelope, distributeResult } = await apiSeedPendingDocument(request, {
|
||||
recipients: [
|
||||
{
|
||||
email: 'partial-assistant@test.documenso.com',
|
||||
name: 'Partial Assistant',
|
||||
role: 'ASSISTANT',
|
||||
signingOrder: 1,
|
||||
},
|
||||
{
|
||||
email: 'partial-assisted-signer@test.documenso.com',
|
||||
name: 'Partial Assisted Signer',
|
||||
role: 'SIGNER',
|
||||
signingOrder: 2,
|
||||
},
|
||||
{
|
||||
email: 'partial-other-signer@test.documenso.com',
|
||||
name: 'Partial Other Signer',
|
||||
role: 'SIGNER',
|
||||
signingOrder: 3,
|
||||
},
|
||||
],
|
||||
fieldsPerRecipient: [
|
||||
[],
|
||||
[
|
||||
{ type: FieldType.SIGNATURE, page: 1, positionX: 5, positionY: 5, width: 15, height: 5 },
|
||||
{ type: FieldType.TEXT, page: 1, positionX: 5, positionY: 15, width: 15, height: 5 },
|
||||
],
|
||||
[{ type: FieldType.SIGNATURE, page: 1, positionX: 5, positionY: 25, width: 15, height: 5 }],
|
||||
],
|
||||
});
|
||||
|
||||
const assistant = distributeResult.recipients.find(
|
||||
(recipient) => recipient.email === 'partial-assistant@test.documenso.com',
|
||||
);
|
||||
const signer = distributeResult.recipients.find(
|
||||
(recipient) => recipient.email === 'partial-assisted-signer@test.documenso.com',
|
||||
);
|
||||
const other = distributeResult.recipients.find(
|
||||
(recipient) => recipient.email === 'partial-other-signer@test.documenso.com',
|
||||
);
|
||||
const textField = envelope.fields.find(
|
||||
(field) => field.recipientId === signer?.id && field.type === FieldType.TEXT,
|
||||
);
|
||||
|
||||
if (!assistant || !signer || !other || !textField) {
|
||||
throw new Error('Expected assistant test fields');
|
||||
}
|
||||
|
||||
await trpcMutation(request, 'field.signFieldWithToken', {
|
||||
token: assistant.token,
|
||||
fieldId: textField.id,
|
||||
value: 'Filled by assistant',
|
||||
isBase64: false,
|
||||
});
|
||||
|
||||
const download = (token: string) =>
|
||||
request.get(
|
||||
`${WEBAPP_BASE_URL}/api/files/token/${token}/envelopeItem/${envelope.envelopeItems[0].id}/download/pending`,
|
||||
);
|
||||
|
||||
const [assistantPdf, signerPdf, otherPdf] = await Promise.all([
|
||||
download(assistant.token),
|
||||
download(signer.token),
|
||||
download(other.token),
|
||||
]);
|
||||
|
||||
expect(assistantPdf.status()).toBe(200);
|
||||
expect(signerPdf.status()).toBe(200);
|
||||
expect(otherPdf.status()).toBe(200);
|
||||
expect(assistantPdf.headers().etag).toBe(signerPdf.headers().etag);
|
||||
expect(assistantPdf.headers().etag).not.toBe(otherPdf.headers().etag);
|
||||
});
|
||||
|
||||
test('rejects a recipient token pending download once the envelope is completed', async ({ request }) => {
|
||||
const { envelope, distributeResult } = await apiSeedPendingDocument(request);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user