mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 09:12:02 +10:00
feat: enhance recipient placeholder extraction and management in PDF processing
This commit is contained in:
@ -13,7 +13,6 @@ import { type TFieldAndMeta, ZFieldAndMetaSchema } from '@documenso/lib/types/fi
|
|||||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import type { EnvelopeIdOptions } from '@documenso/lib/utils/envelope';
|
import type { EnvelopeIdOptions } from '@documenso/lib/utils/envelope';
|
||||||
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
|
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
|
||||||
import { generateRecipientPlaceholder } from '@documenso/lib/utils/templates';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
import { getPageSize } from './get-page-size';
|
import { getPageSize } from './get-page-size';
|
||||||
@ -43,6 +42,7 @@ type PlaceholderInfo = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type FieldToCreate = TFieldAndMeta & {
|
type FieldToCreate = TFieldAndMeta & {
|
||||||
|
envelopeItemId?: string;
|
||||||
recipientId: number;
|
recipientId: number;
|
||||||
pageNumber: number;
|
pageNumber: number;
|
||||||
pageX: number;
|
pageX: number;
|
||||||
@ -51,6 +51,12 @@ type FieldToCreate = TFieldAndMeta & {
|
|||||||
height: number;
|
height: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type RecipientPlaceholderInfo = {
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
recipientIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Questions for later:
|
Questions for later:
|
||||||
- Does it handle multi-page PDFs? ✅ YES! ✅
|
- Does it handle multi-page PDFs? ✅ YES! ✅
|
||||||
@ -316,9 +322,7 @@ export const replacePlaceholdersInPDF = async (pdf: Buffer): Promise<Buffer> =>
|
|||||||
return Buffer.from(modifiedPdfBytes);
|
return Buffer.from(modifiedPdfBytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
const extractRecipientPlaceholder = (
|
const extractRecipientPlaceholder = (placeholder: string): RecipientPlaceholderInfo => {
|
||||||
placeholder: string,
|
|
||||||
): { email: string; recipientIndex: number } => {
|
|
||||||
const indexMatch = placeholder.match(/^r(\d+)$/i);
|
const indexMatch = placeholder.match(/^r(\d+)$/i);
|
||||||
|
|
||||||
if (!indexMatch) {
|
if (!indexMatch) {
|
||||||
@ -327,9 +331,12 @@ const extractRecipientPlaceholder = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const recipientIndex = Number(indexMatch[1]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
email: `recipient.${indexMatch[1]}@documenso.com`,
|
email: `recipient.${recipientIndex}@documenso.com`,
|
||||||
recipientIndex: Number(indexMatch[1]),
|
name: `Recipient ${recipientIndex}`,
|
||||||
|
recipientIndex,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -339,6 +346,7 @@ export const insertFieldsFromPlaceholdersInPDF = async (
|
|||||||
teamId: number,
|
teamId: number,
|
||||||
envelopeId: EnvelopeIdOptions,
|
envelopeId: EnvelopeIdOptions,
|
||||||
requestMetadata: ApiRequestMetadata,
|
requestMetadata: ApiRequestMetadata,
|
||||||
|
envelopeItemId?: string,
|
||||||
): Promise<Buffer> => {
|
): Promise<Buffer> => {
|
||||||
const placeholders = await extractPlaceholdersFromPDF(pdf);
|
const placeholders = await extractPlaceholdersFromPDF(pdf);
|
||||||
|
|
||||||
@ -347,15 +355,15 @@ export const insertFieldsFromPlaceholdersInPDF = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A structure that maps the recipient email to the recipient index.
|
A structure that maps the recipient index to the recipient name.
|
||||||
Example: 'recipient.1@documenso.com' => 1
|
Example: 1 => 'Recipient 1'
|
||||||
*/
|
*/
|
||||||
const recipientEmailToIndex = new Map<string, number>();
|
const recipientPlaceholders = new Map<number, string>();
|
||||||
|
|
||||||
for (const placeholder of placeholders) {
|
for (const placeholder of placeholders) {
|
||||||
const { email, recipientIndex } = extractRecipientPlaceholder(placeholder.recipient);
|
const { name, recipientIndex } = extractRecipientPlaceholder(placeholder.recipient);
|
||||||
|
|
||||||
recipientEmailToIndex.set(email, recipientIndex);
|
recipientPlaceholders.set(recipientIndex, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -363,13 +371,11 @@ export const insertFieldsFromPlaceholdersInPDF = async (
|
|||||||
Example: [{ email: 'recipient.1@documenso.com', name: 'Recipient 1', role: 'SIGNER', signingOrder: 1 }]
|
Example: [{ email: 'recipient.1@documenso.com', name: 'Recipient 1', role: 'SIGNER', signingOrder: 1 }]
|
||||||
*/
|
*/
|
||||||
const recipientsToCreate = Array.from(
|
const recipientsToCreate = Array.from(
|
||||||
recipientEmailToIndex.entries(),
|
recipientPlaceholders.entries(),
|
||||||
([email, recipientIndex]) => {
|
([recipientIndex, name]) => {
|
||||||
const placeholderInfo = generateRecipientPlaceholder(recipientIndex);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
email,
|
email: `recipient.${recipientIndex}@documenso.com`,
|
||||||
name: placeholderInfo.name,
|
name,
|
||||||
role: RecipientRole.SIGNER,
|
role: RecipientRole.SIGNER,
|
||||||
signingOrder: recipientIndex,
|
signingOrder: recipientIndex,
|
||||||
};
|
};
|
||||||
@ -398,18 +404,34 @@ export const insertFieldsFromPlaceholdersInPDF = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let createdRecipients: Pick<Recipient, 'id' | 'email'>[];
|
const existingRecipients = await prisma.recipient.findMany({
|
||||||
|
where: {
|
||||||
|
envelopeId: envelope.id,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingEmails = new Set(existingRecipients.map((r) => r.email.toLowerCase()));
|
||||||
|
const recipientsToCreateFiltered = recipientsToCreate.filter(
|
||||||
|
(r) => !existingEmails.has(r.email.toLowerCase()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let createdRecipients: Pick<Recipient, 'id' | 'email'>[] = existingRecipients;
|
||||||
|
|
||||||
|
if (recipientsToCreateFiltered.length > 0) {
|
||||||
if (envelope.type === EnvelopeType.DOCUMENT) {
|
if (envelope.type === EnvelopeType.DOCUMENT) {
|
||||||
const { recipients } = await createDocumentRecipients({
|
const { recipients } = await createDocumentRecipients({
|
||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
id: envelopeId,
|
id: envelopeId,
|
||||||
recipients: recipientsToCreate,
|
recipients: recipientsToCreateFiltered,
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
createdRecipients = recipients;
|
createdRecipients = [...existingRecipients, ...recipients];
|
||||||
} else if (envelope.type === EnvelopeType.TEMPLATE) {
|
} else if (envelope.type === EnvelopeType.TEMPLATE) {
|
||||||
const templateId =
|
const templateId =
|
||||||
envelopeId.type === 'templateId'
|
envelopeId.type === 'templateId'
|
||||||
@ -420,15 +442,16 @@ export const insertFieldsFromPlaceholdersInPDF = async (
|
|||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
templateId,
|
templateId,
|
||||||
recipients: recipientsToCreate,
|
recipients: recipientsToCreateFiltered,
|
||||||
});
|
});
|
||||||
|
|
||||||
createdRecipients = recipients;
|
createdRecipients = [...existingRecipients, ...recipients];
|
||||||
} else {
|
} else {
|
||||||
throw new AppError(AppErrorCode.INVALID_BODY, {
|
throw new AppError(AppErrorCode.INVALID_BODY, {
|
||||||
message: `Invalid envelope type: ${envelope.type}`,
|
message: `Invalid envelope type: ${envelope.type}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const fieldsToCreate: FieldToCreate[] = [];
|
const fieldsToCreate: FieldToCreate[] = [];
|
||||||
|
|
||||||
@ -461,6 +484,7 @@ export const insertFieldsFromPlaceholdersInPDF = async (
|
|||||||
|
|
||||||
fieldsToCreate.push({
|
fieldsToCreate.push({
|
||||||
...placeholder.fieldAndMeta,
|
...placeholder.fieldAndMeta,
|
||||||
|
envelopeItemId,
|
||||||
recipientId,
|
recipientId,
|
||||||
pageNumber: placeholder.page,
|
pageNumber: placeholder.page,
|
||||||
pageX: xPercent,
|
pageX: xPercent,
|
||||||
|
|||||||
Reference in New Issue
Block a user