mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 17:21:41 +10:00
fix: merge conflicts
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import type { DocumentDistributionMethod, DocumentSigningOrder } from '@prisma/client';
|
||||
import {
|
||||
DocumentSource,
|
||||
EnvelopeType,
|
||||
type Field,
|
||||
FolderType,
|
||||
type Recipient,
|
||||
@ -37,9 +38,11 @@ import {
|
||||
} from '../../types/field-meta';
|
||||
import {
|
||||
ZWebhookDocumentSchema,
|
||||
mapDocumentToWebhookDocumentPayload,
|
||||
mapEnvelopeToWebhookDocumentPayload,
|
||||
} from '../../types/webhook-payload';
|
||||
import type { ApiRequestMetadata } from '../../universal/extract-request-metadata';
|
||||
import { getFileServerSide } from '../../universal/upload/get-file.server';
|
||||
import { putPdfFileServerSide } from '../../universal/upload/put-file.server';
|
||||
import { extractDerivedDocumentMeta } from '../../utils/document';
|
||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||
import {
|
||||
@ -47,8 +50,12 @@ import {
|
||||
createRecipientAuthOptions,
|
||||
extractDocumentAuthMethods,
|
||||
} from '../../utils/document-auth';
|
||||
import type { EnvelopeIdOptions } from '../../utils/envelope';
|
||||
import { mapSecondaryIdToTemplateId } from '../../utils/envelope';
|
||||
import { calculateRecipientExpiry } from '../../utils/expiry';
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
import { getEnvelopeWhereInput } from '../envelope/get-envelope-by-id';
|
||||
import { incrementDocumentId } from '../envelope/increment-id';
|
||||
import { getTeamSettings } from '../team/get-team-settings';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
|
||||
@ -61,7 +68,7 @@ type FinalRecipient = Pick<
|
||||
};
|
||||
|
||||
export type CreateDocumentFromTemplateOptions = {
|
||||
templateId: number;
|
||||
id: EnvelopeIdOptions;
|
||||
externalId?: string | null;
|
||||
userId: number;
|
||||
teamId: number;
|
||||
@ -73,7 +80,17 @@ export type CreateDocumentFromTemplateOptions = {
|
||||
}[];
|
||||
folderId?: string;
|
||||
prefillFields?: TFieldMetaPrefillFieldsSchema[];
|
||||
customDocumentDataId?: string;
|
||||
|
||||
customDocumentData?: {
|
||||
documentDataId: string;
|
||||
|
||||
/**
|
||||
* The envelope item ID which will be updated to use the custom document data.
|
||||
*
|
||||
* If undefined, will use the first envelope item. This is done for backwards compatibility reasons.
|
||||
*/
|
||||
envelopeItemId?: string;
|
||||
}[];
|
||||
|
||||
/**
|
||||
* Values that will override the predefined values in the template.
|
||||
@ -271,30 +288,38 @@ const getUpdatedFieldMeta = (field: Field, prefillField?: TFieldMetaPrefillField
|
||||
};
|
||||
|
||||
export const createDocumentFromTemplate = async ({
|
||||
templateId,
|
||||
id,
|
||||
externalId,
|
||||
userId,
|
||||
teamId,
|
||||
recipients,
|
||||
customDocumentDataId,
|
||||
customDocumentData = [],
|
||||
override,
|
||||
requestMetadata,
|
||||
folderId,
|
||||
prefillFields,
|
||||
}: CreateDocumentFromTemplateOptions) => {
|
||||
const template = await prisma.template.findUnique({
|
||||
where: {
|
||||
id: templateId,
|
||||
team: buildTeamWhereQuery({ teamId, userId }),
|
||||
},
|
||||
const { envelopeWhereInput } = await getEnvelopeWhereInput({
|
||||
id,
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
const template = await prisma.envelope.findUnique({
|
||||
where: envelopeWhereInput,
|
||||
include: {
|
||||
recipients: {
|
||||
include: {
|
||||
fields: true,
|
||||
},
|
||||
},
|
||||
templateDocumentData: true,
|
||||
templateMeta: true,
|
||||
envelopeItems: {
|
||||
include: {
|
||||
documentData: true,
|
||||
},
|
||||
},
|
||||
documentMeta: true,
|
||||
},
|
||||
});
|
||||
|
||||
@ -320,6 +345,15 @@ export const createDocumentFromTemplate = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const legacyTemplateId = mapSecondaryIdToTemplateId(template.secondaryId);
|
||||
const finalEnvelopeTitle = override?.title || template.title;
|
||||
|
||||
if (template.envelopeItems.length < 1) {
|
||||
throw new AppError(AppErrorCode.INVALID_BODY, {
|
||||
message: 'Template must have at least 1 envelope item',
|
||||
});
|
||||
}
|
||||
|
||||
const settings = await getTeamSettings({
|
||||
userId,
|
||||
teamId,
|
||||
@ -357,87 +391,130 @@ export const createDocumentFromTemplate = async ({
|
||||
};
|
||||
});
|
||||
|
||||
let parentDocumentData = template.templateDocumentData;
|
||||
const firstEnvelopeItemId = template.envelopeItems[0].id;
|
||||
|
||||
if (customDocumentDataId) {
|
||||
const customDocumentData = await prisma.documentData.findFirst({
|
||||
where: {
|
||||
id: customDocumentDataId,
|
||||
},
|
||||
});
|
||||
// Key = original envelope item ID
|
||||
// Value = duplicated envelope item ID.
|
||||
const oldEnvelopeItemToNewEnvelopeItemIdMap: Record<string, string> = {};
|
||||
|
||||
if (!customDocumentData) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Custom document data not found',
|
||||
// Duplicate the envelope item data.
|
||||
// Note: This is duplicated in createDocumentFromDirectTemplate
|
||||
const envelopeItemsToCreate = await Promise.all(
|
||||
template.envelopeItems.map(async (item, i) => {
|
||||
let documentDataIdToDuplicate = item.documentDataId;
|
||||
|
||||
const foundCustomDocumentData = customDocumentData.find(
|
||||
(customDocumentDataItem) =>
|
||||
customDocumentDataItem.envelopeItemId || firstEnvelopeItemId === item.id,
|
||||
);
|
||||
|
||||
if (foundCustomDocumentData) {
|
||||
documentDataIdToDuplicate = foundCustomDocumentData.documentDataId;
|
||||
}
|
||||
|
||||
const documentDataToDuplicate = await prisma.documentData.findFirst({
|
||||
where: {
|
||||
id: documentDataIdToDuplicate,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
parentDocumentData = customDocumentData;
|
||||
}
|
||||
if (!documentDataToDuplicate) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Document data not found',
|
||||
});
|
||||
}
|
||||
|
||||
const documentData = await prisma.documentData.create({
|
||||
data: {
|
||||
type: parentDocumentData.type,
|
||||
data: parentDocumentData.data,
|
||||
initialData: parentDocumentData.initialData,
|
||||
},
|
||||
const buffer = await getFileServerSide(documentDataToDuplicate);
|
||||
|
||||
const titleToUse = item.title || finalEnvelopeTitle;
|
||||
|
||||
const duplicatedFile = await putPdfFileServerSide({
|
||||
name: titleToUse,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(buffer),
|
||||
});
|
||||
|
||||
const newDocumentData = await prisma.documentData.create({
|
||||
data: {
|
||||
type: duplicatedFile.type,
|
||||
data: duplicatedFile.data,
|
||||
initialData: duplicatedFile.initialData,
|
||||
},
|
||||
});
|
||||
|
||||
const newEnvelopeItemId = prefixedId('envelope_item');
|
||||
|
||||
oldEnvelopeItemToNewEnvelopeItemIdMap[item.id] = newEnvelopeItemId;
|
||||
|
||||
return {
|
||||
id: newEnvelopeItemId,
|
||||
title: titleToUse.endsWith('.pdf') ? titleToUse.slice(0, -4) : titleToUse,
|
||||
documentDataId: newDocumentData.id,
|
||||
order: item.order !== undefined ? item.order : i + 1,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const incrementedDocumentId = await incrementDocumentId();
|
||||
|
||||
const documentMeta = await prisma.documentMeta.create({
|
||||
data: extractDerivedDocumentMeta(settings, {
|
||||
subject: override?.subject || template.documentMeta?.subject,
|
||||
message: override?.message || template.documentMeta?.message,
|
||||
timezone: override?.timezone || template.documentMeta?.timezone,
|
||||
dateFormat: override?.dateFormat || template.documentMeta?.dateFormat,
|
||||
redirectUrl: override?.redirectUrl || template.documentMeta?.redirectUrl,
|
||||
distributionMethod: override?.distributionMethod || template.documentMeta?.distributionMethod,
|
||||
emailSettings: override?.emailSettings || template.documentMeta?.emailSettings,
|
||||
signingOrder: override?.signingOrder || template.documentMeta?.signingOrder,
|
||||
language: override?.language || template.documentMeta?.language || settings.documentLanguage,
|
||||
typedSignatureEnabled:
|
||||
override?.typedSignatureEnabled ?? template.documentMeta?.typedSignatureEnabled,
|
||||
uploadSignatureEnabled:
|
||||
override?.uploadSignatureEnabled ?? template.documentMeta?.uploadSignatureEnabled,
|
||||
drawSignatureEnabled:
|
||||
override?.drawSignatureEnabled ?? template.documentMeta?.drawSignatureEnabled,
|
||||
allowDictateNextSigner:
|
||||
override?.allowDictateNextSigner ?? template.documentMeta?.allowDictateNextSigner,
|
||||
}),
|
||||
});
|
||||
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const document = await tx.document.create({
|
||||
const envelope = await tx.envelope.create({
|
||||
data: {
|
||||
id: prefixedId('envelope'),
|
||||
secondaryId: incrementedDocumentId.formattedDocumentId,
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
internalVersion: template.internalVersion,
|
||||
qrToken: prefixedId('qr'),
|
||||
source: DocumentSource.TEMPLATE,
|
||||
externalId: externalId || template.externalId,
|
||||
templateId: template.id,
|
||||
templateId: legacyTemplateId, // The template this envelope was created from.
|
||||
userId,
|
||||
folderId,
|
||||
teamId: template.teamId,
|
||||
title: override?.title || template.title,
|
||||
documentDataId: documentData.id,
|
||||
title: finalEnvelopeTitle,
|
||||
envelopeItems: {
|
||||
createMany: {
|
||||
data: envelopeItemsToCreate,
|
||||
},
|
||||
},
|
||||
authOptions: createDocumentAuthOptions({
|
||||
globalAccessAuth: templateAuthOptions.globalAccessAuth,
|
||||
globalActionAuth: templateAuthOptions.globalActionAuth,
|
||||
}),
|
||||
visibility: template.visibility || settings.documentVisibility,
|
||||
useLegacyFieldInsertion: template.useLegacyFieldInsertion ?? false,
|
||||
documentMeta: {
|
||||
create: extractDerivedDocumentMeta(settings, {
|
||||
subject: override?.subject || template.templateMeta?.subject,
|
||||
message: override?.message || template.templateMeta?.message,
|
||||
timezone: override?.timezone || template.templateMeta?.timezone,
|
||||
password: override?.password || template.templateMeta?.password,
|
||||
dateFormat: override?.dateFormat || template.templateMeta?.dateFormat,
|
||||
redirectUrl: override?.redirectUrl || template.templateMeta?.redirectUrl,
|
||||
distributionMethod:
|
||||
override?.distributionMethod || template.templateMeta?.distributionMethod,
|
||||
emailSettings: override?.emailSettings || template.templateMeta?.emailSettings,
|
||||
signingOrder: override?.signingOrder || template.templateMeta?.signingOrder,
|
||||
language:
|
||||
override?.language || template.templateMeta?.language || settings.documentLanguage,
|
||||
typedSignatureEnabled:
|
||||
override?.typedSignatureEnabled ?? template.templateMeta?.typedSignatureEnabled,
|
||||
uploadSignatureEnabled:
|
||||
override?.uploadSignatureEnabled ?? template.templateMeta?.uploadSignatureEnabled,
|
||||
drawSignatureEnabled:
|
||||
override?.drawSignatureEnabled ?? template.templateMeta?.drawSignatureEnabled,
|
||||
allowDictateNextSigner:
|
||||
override?.allowDictateNextSigner ?? template.templateMeta?.allowDictateNextSigner,
|
||||
defaultExpiryAmount:
|
||||
override?.expiryAmount ?? template.templateMeta?.defaultExpiryAmount,
|
||||
defaultExpiryUnit: override?.expiryUnit ?? template.templateMeta?.defaultExpiryUnit,
|
||||
}),
|
||||
},
|
||||
documentMetaId: documentMeta.id,
|
||||
recipients: {
|
||||
createMany: {
|
||||
data: finalRecipients.map((recipient) => {
|
||||
const authOptions = ZRecipientAuthOptionsSchema.parse(recipient?.authOptions);
|
||||
|
||||
// Calculate expiry date based on template defaults
|
||||
const expiryAmount =
|
||||
override?.expiryAmount ?? template.templateMeta?.defaultExpiryAmount ?? null;
|
||||
const expiryUnit =
|
||||
override?.expiryUnit ?? template.templateMeta?.defaultExpiryUnit ?? null;
|
||||
// Calculate expiry date based on override
|
||||
// Note: Templates no longer have default expiry settings (TemplateMeta removed)
|
||||
const expiryAmount = override?.expiryAmount ?? null;
|
||||
const expiryUnit = override?.expiryUnit ?? null;
|
||||
const recipientExpiryDate = calculateRecipientExpiry(
|
||||
expiryAmount,
|
||||
expiryUnit,
|
||||
@ -472,11 +549,15 @@ export const createDocumentFromTemplate = async ({
|
||||
id: 'asc',
|
||||
},
|
||||
},
|
||||
documentData: true,
|
||||
envelopeItems: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let fieldsToCreate: Omit<Field, 'id' | 'secondaryId' | 'templateId'>[] = [];
|
||||
let fieldsToCreate: Omit<Field, 'id' | 'secondaryId'>[] = [];
|
||||
|
||||
// Get all template field IDs first so we can validate later
|
||||
const allTemplateFieldIds = finalRecipients.flatMap((recipient) =>
|
||||
@ -520,7 +601,7 @@ export const createDocumentFromTemplate = async ({
|
||||
}
|
||||
|
||||
Object.values(finalRecipients).forEach(({ token, fields }) => {
|
||||
const recipient = document.recipients.find((recipient) => recipient.token === token);
|
||||
const recipient = envelope.recipients.find((recipient) => recipient.token === token);
|
||||
|
||||
if (!recipient) {
|
||||
throw new Error('Recipient not found.');
|
||||
@ -531,7 +612,8 @@ export const createDocumentFromTemplate = async ({
|
||||
const prefillField = prefillFields?.find((value) => value.id === field.id);
|
||||
|
||||
const payload = {
|
||||
documentId: document.id,
|
||||
envelopeItemId: oldEnvelopeItemToNewEnvelopeItemIdMap[field.envelopeItemId],
|
||||
envelopeId: envelope.id,
|
||||
recipientId: recipient.id,
|
||||
type: field.type,
|
||||
page: field.page,
|
||||
@ -562,7 +644,7 @@ export const createDocumentFromTemplate = async ({
|
||||
}
|
||||
|
||||
payload.customText = DateTime.fromJSDate(date).toFormat(
|
||||
template.templateMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT,
|
||||
template.documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT,
|
||||
);
|
||||
|
||||
payload.inserted = true;
|
||||
@ -587,21 +669,21 @@ export const createDocumentFromTemplate = async ({
|
||||
await tx.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
|
||||
documentId: document.id,
|
||||
envelopeId: envelope.id,
|
||||
metadata: requestMetadata,
|
||||
data: {
|
||||
title: document.title,
|
||||
title: envelope.title,
|
||||
source: {
|
||||
type: DocumentSource.TEMPLATE,
|
||||
templateId: template.id,
|
||||
templateId: legacyTemplateId,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const createdDocument = await tx.document.findFirst({
|
||||
const createdEnvelope = await tx.envelope.findFirst({
|
||||
where: {
|
||||
id: document.id,
|
||||
id: envelope.id,
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
@ -609,17 +691,17 @@ export const createDocumentFromTemplate = async ({
|
||||
},
|
||||
});
|
||||
|
||||
if (!createdDocument) {
|
||||
if (!createdEnvelope) {
|
||||
throw new Error('Document not found');
|
||||
}
|
||||
|
||||
await triggerWebhook({
|
||||
event: WebhookTriggerEvents.DOCUMENT_CREATED,
|
||||
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(createdDocument)),
|
||||
data: ZWebhookDocumentSchema.parse(mapEnvelopeToWebhookDocumentPayload(createdEnvelope)),
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
return document;
|
||||
return envelope;
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user