mirror of
https://github.com/documenso/documenso.git
synced 2025-11-19 19:21:39 +10:00
feat: migrate templates and documents to envelope model
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { ReadStatus, SendStatus, SigningStatus } from '@prisma/client';
|
||||
import { EnvelopeType, ReadStatus, SendStatus, SigningStatus } from '@prisma/client';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
|
||||
@ -11,6 +11,7 @@ import { getI18nInstance } from '../../../client-only/providers/i18n-server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { getEmailContext } from '../../../server-only/email/get-email-context';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { unsafeBuildEnvelopeIdQuery } from '../../../utils/envelope';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendDocumentCancelledEmailsJobDefinition } from './send-document-cancelled-emails';
|
||||
@ -24,10 +25,14 @@ export const run = async ({
|
||||
}) => {
|
||||
const { documentId, cancellationReason } = payload;
|
||||
|
||||
const document = await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
},
|
||||
const envelope = await prisma.envelope.findFirstOrThrow({
|
||||
where: unsafeBuildEnvelopeIdQuery(
|
||||
{
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
EnvelopeType.DOCUMENT,
|
||||
),
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
@ -52,12 +57,12 @@ export const run = async ({
|
||||
emailType: 'RECIPIENT',
|
||||
source: {
|
||||
type: 'team',
|
||||
teamId: document.teamId,
|
||||
teamId: envelope.teamId,
|
||||
},
|
||||
meta: document.documentMeta,
|
||||
meta: envelope.documentMeta,
|
||||
});
|
||||
|
||||
const { documentMeta, user: documentOwner } = document;
|
||||
const { documentMeta, user: documentOwner } = envelope;
|
||||
|
||||
// Check if document cancellation emails are enabled
|
||||
const isEmailEnabled = extractDerivedDocumentEmailSettings(documentMeta).documentDeleted;
|
||||
@ -69,7 +74,7 @@ export const run = async ({
|
||||
const i18n = await getI18nInstance(emailLanguage);
|
||||
|
||||
// Send cancellation emails to all recipients who have been sent the document or viewed it
|
||||
const recipientsToNotify = document.recipients.filter(
|
||||
const recipientsToNotify = envelope.recipients.filter(
|
||||
(recipient) =>
|
||||
(recipient.sendStatus === SendStatus.SENT || recipient.readStatus === ReadStatus.OPENED) &&
|
||||
recipient.signingStatus !== SigningStatus.REJECTED,
|
||||
@ -79,7 +84,7 @@ export const run = async ({
|
||||
await Promise.all(
|
||||
recipientsToNotify.map(async (recipient) => {
|
||||
const template = createElement(DocumentCancelTemplate, {
|
||||
documentName: document.title,
|
||||
documentName: envelope.title,
|
||||
inviterName: documentOwner.name || undefined,
|
||||
inviterEmail: documentOwner.email,
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
@ -102,7 +107,7 @@ export const run = async ({
|
||||
},
|
||||
from: senderEmail,
|
||||
replyTo: replyToEmail,
|
||||
subject: i18n._(msg`Document "${document.title}" Cancelled`),
|
||||
subject: i18n._(msg`Document "${envelope.title}" Cancelled`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import { DocumentRecipientSignedEmailTemplate } from '@documenso/email/templates/document-recipient-signed';
|
||||
@ -10,6 +11,7 @@ import { getI18nInstance } from '../../../client-only/providers/i18n-server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { getEmailContext } from '../../../server-only/email/get-email-context';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { unsafeBuildEnvelopeIdQuery } from '../../../utils/envelope';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendRecipientSignedEmailJobDefinition } from './send-recipient-signed-email';
|
||||
@ -23,9 +25,15 @@ export const run = async ({
|
||||
}) => {
|
||||
const { documentId, recipientId } = payload;
|
||||
|
||||
const document = await prisma.document.findFirst({
|
||||
const envelope = await prisma.envelope.findFirst({
|
||||
where: {
|
||||
id: documentId,
|
||||
...unsafeBuildEnvelopeIdQuery(
|
||||
{
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
EnvelopeType.DOCUMENT,
|
||||
),
|
||||
recipients: {
|
||||
some: {
|
||||
id: recipientId,
|
||||
@ -49,25 +57,25 @@ export const run = async ({
|
||||
},
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
if (!envelope) {
|
||||
throw new Error('Document not found');
|
||||
}
|
||||
|
||||
if (document.recipients.length === 0) {
|
||||
if (envelope.recipients.length === 0) {
|
||||
throw new Error('Document has no recipients');
|
||||
}
|
||||
|
||||
const isRecipientSignedEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
envelope.documentMeta,
|
||||
).recipientSigned;
|
||||
|
||||
if (!isRecipientSignedEmailEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [recipient] = document.recipients;
|
||||
const [recipient] = envelope.recipients;
|
||||
const { email: recipientEmail, name: recipientName } = recipient;
|
||||
const { user: owner } = document;
|
||||
const { user: owner } = envelope;
|
||||
|
||||
const recipientReference = recipientName || recipientEmail;
|
||||
|
||||
@ -80,9 +88,9 @@ export const run = async ({
|
||||
emailType: 'INTERNAL',
|
||||
source: {
|
||||
type: 'team',
|
||||
teamId: document.teamId,
|
||||
teamId: envelope.teamId,
|
||||
},
|
||||
meta: document.documentMeta,
|
||||
meta: envelope.documentMeta,
|
||||
});
|
||||
|
||||
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
||||
@ -90,7 +98,7 @@ export const run = async ({
|
||||
const i18n = await getI18nInstance(emailLanguage);
|
||||
|
||||
const template = createElement(DocumentRecipientSignedEmailTemplate, {
|
||||
documentName: document.title,
|
||||
documentName: envelope.title,
|
||||
recipientName,
|
||||
recipientEmail,
|
||||
assetBaseUrl,
|
||||
@ -112,7 +120,7 @@ export const run = async ({
|
||||
address: owner.email,
|
||||
},
|
||||
from: senderEmail,
|
||||
subject: i18n._(msg`${recipientReference} has signed "${document.title}"`),
|
||||
subject: i18n._(msg`${recipientReference} has signed "${envelope.title}"`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { SendStatus, SigningStatus } from '@prisma/client';
|
||||
import { EnvelopeType, SendStatus, SigningStatus } from '@prisma/client';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import DocumentRejectedEmail from '@documenso/email/templates/document-rejected';
|
||||
@ -13,6 +13,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { DOCUMENSO_INTERNAL_EMAIL } from '../../../constants/email';
|
||||
import { getEmailContext } from '../../../server-only/email/get-email-context';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { unsafeBuildEnvelopeIdQuery } from '../../../utils/envelope';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { formatDocumentsPath } from '../../../utils/teams';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
@ -27,11 +28,15 @@ export const run = async ({
|
||||
}) => {
|
||||
const { documentId, recipientId } = payload;
|
||||
|
||||
const [document, recipient] = await Promise.all([
|
||||
prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
},
|
||||
const [envelope, recipient] = await Promise.all([
|
||||
prisma.envelope.findFirstOrThrow({
|
||||
where: unsafeBuildEnvelopeIdQuery(
|
||||
{
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
EnvelopeType.DOCUMENT,
|
||||
),
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
@ -58,10 +63,10 @@ export const run = async ({
|
||||
}),
|
||||
]);
|
||||
|
||||
const { user: documentOwner } = document;
|
||||
const { user: documentOwner } = envelope;
|
||||
|
||||
const isEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
envelope.documentMeta,
|
||||
).recipientSigningRequest;
|
||||
|
||||
if (!isEmailEnabled) {
|
||||
@ -72,9 +77,9 @@ export const run = async ({
|
||||
emailType: 'RECIPIENT',
|
||||
source: {
|
||||
type: 'team',
|
||||
teamId: document.teamId,
|
||||
teamId: envelope.teamId,
|
||||
},
|
||||
meta: document.documentMeta,
|
||||
meta: envelope.documentMeta,
|
||||
});
|
||||
|
||||
const i18n = await getI18nInstance(emailLanguage);
|
||||
@ -83,8 +88,8 @@ export const run = async ({
|
||||
await io.runTask('send-rejection-confirmation-email', async () => {
|
||||
const recipientTemplate = createElement(DocumentRejectionConfirmedEmail, {
|
||||
recipientName: recipient.name,
|
||||
documentName: document.title,
|
||||
documentOwnerName: document.user.name || document.user.email,
|
||||
documentName: envelope.title,
|
||||
documentOwnerName: envelope.user.name || envelope.user.email,
|
||||
reason: recipient.rejectionReason || '',
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
});
|
||||
@ -105,7 +110,7 @@ export const run = async ({
|
||||
},
|
||||
from: senderEmail,
|
||||
replyTo: replyToEmail,
|
||||
subject: i18n._(msg`Document "${document.title}" - Rejection Confirmed`),
|
||||
subject: i18n._(msg`Document "${envelope.title}" - Rejection Confirmed`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
@ -115,9 +120,9 @@ export const run = async ({
|
||||
await io.runTask('send-owner-notification-email', async () => {
|
||||
const ownerTemplate = createElement(DocumentRejectedEmail, {
|
||||
recipientName: recipient.name,
|
||||
documentName: document.title,
|
||||
documentUrl: `${NEXT_PUBLIC_WEBAPP_URL()}${formatDocumentsPath(document.team?.url)}/${
|
||||
document.id
|
||||
documentName: envelope.title,
|
||||
documentUrl: `${NEXT_PUBLIC_WEBAPP_URL()}${formatDocumentsPath(envelope.team?.url)}/${
|
||||
envelope.id
|
||||
}`,
|
||||
rejectionReason: recipient.rejectionReason || '',
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
@ -138,7 +143,7 @@ export const run = async ({
|
||||
address: documentOwner.email,
|
||||
},
|
||||
from: DOCUMENSO_INTERNAL_EMAIL, // Purposefully using internal email here.
|
||||
subject: i18n._(msg`Document "${document.title}" - Rejected by ${recipient.name}`),
|
||||
subject: i18n._(msg`Document "${envelope.title}" - Rejected by ${recipient.name}`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
DocumentSource,
|
||||
DocumentStatus,
|
||||
EnvelopeType,
|
||||
OrganisationType,
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
@ -23,6 +24,7 @@ import { getEmailContext } from '../../../server-only/email/get-email-context';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
|
||||
import { unsafeBuildEnvelopeIdQuery } from '../../../utils/envelope';
|
||||
import { renderCustomEmailTemplate } from '../../../utils/render-custom-email-template';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
@ -37,7 +39,7 @@ export const run = async ({
|
||||
}) => {
|
||||
const { userId, documentId, recipientId, requestMetadata } = payload;
|
||||
|
||||
const [user, document, recipient] = await Promise.all([
|
||||
const [user, envelope, recipient] = await Promise.all([
|
||||
prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
@ -48,9 +50,15 @@ export const run = async ({
|
||||
name: true,
|
||||
},
|
||||
}),
|
||||
prisma.document.findFirstOrThrow({
|
||||
prisma.envelope.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
...unsafeBuildEnvelopeIdQuery(
|
||||
{
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
EnvelopeType.DOCUMENT,
|
||||
),
|
||||
status: DocumentStatus.PENDING,
|
||||
},
|
||||
include: {
|
||||
@ -70,14 +78,14 @@ export const run = async ({
|
||||
}),
|
||||
]);
|
||||
|
||||
const { documentMeta, team } = document;
|
||||
const { documentMeta, team } = envelope;
|
||||
|
||||
if (recipient.role === RecipientRole.CC) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isRecipientSigningRequestEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
envelope.documentMeta,
|
||||
).recipientSigningRequest;
|
||||
|
||||
if (!isRecipientSigningRequestEmailEnabled) {
|
||||
@ -89,13 +97,13 @@ export const run = async ({
|
||||
emailType: 'RECIPIENT',
|
||||
source: {
|
||||
type: 'team',
|
||||
teamId: document.teamId,
|
||||
teamId: envelope.teamId,
|
||||
},
|
||||
meta: document.documentMeta,
|
||||
meta: envelope.documentMeta,
|
||||
});
|
||||
|
||||
const customEmail = document?.documentMeta;
|
||||
const isDirectTemplate = document.source === DocumentSource.TEMPLATE_DIRECT_LINK;
|
||||
const customEmail = envelope?.documentMeta;
|
||||
const isDirectTemplate = envelope.source === DocumentSource.TEMPLATE_DIRECT_LINK;
|
||||
|
||||
const recipientEmailType = RECIPIENT_ROLE_TO_EMAIL_TYPE[recipient.role];
|
||||
|
||||
@ -113,7 +121,7 @@ export const run = async ({
|
||||
|
||||
if (selfSigner) {
|
||||
emailMessage = i18n._(
|
||||
msg`You have initiated the document ${`"${document.title}"`} that requires you to ${recipientActionVerb} it.`,
|
||||
msg`You have initiated the document ${`"${envelope.title}"`} that requires you to ${recipientActionVerb} it.`,
|
||||
);
|
||||
emailSubject = i18n._(msg`Please ${recipientActionVerb} your document`);
|
||||
}
|
||||
@ -136,8 +144,8 @@ export const run = async ({
|
||||
|
||||
emailMessage = i18n._(
|
||||
settings.includeSenderDetails
|
||||
? msg`${inviterName} on behalf of "${team.name}" has invited you to ${recipientActionVerb} the document "${document.title}".`
|
||||
: msg`${team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`,
|
||||
? msg`${inviterName} on behalf of "${team.name}" has invited you to ${recipientActionVerb} the document "${envelope.title}".`
|
||||
: msg`${team.name} has invited you to ${recipientActionVerb} the document "${envelope.title}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -145,14 +153,14 @@ export const run = async ({
|
||||
const customEmailTemplate = {
|
||||
'signer.name': name,
|
||||
'signer.email': email,
|
||||
'document.name': document.title,
|
||||
'document.name': envelope.title,
|
||||
};
|
||||
|
||||
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
||||
const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`;
|
||||
|
||||
const template = createElement(DocumentInviteEmailTemplate, {
|
||||
documentName: document.title,
|
||||
documentName: envelope.title,
|
||||
inviterName: user.name || undefined,
|
||||
inviterEmail:
|
||||
organisationType === OrganisationType.ORGANISATION
|
||||
@ -210,7 +218,7 @@ export const run = async ({
|
||||
await prisma.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||
documentId: document.id,
|
||||
envelopeId: envelope.id,
|
||||
user,
|
||||
requestMetadata,
|
||||
data: {
|
||||
|
||||
@ -99,9 +99,12 @@ export const run = async ({
|
||||
}
|
||||
}
|
||||
|
||||
const document = await io.runTask(`create-document-${rowIndex}`, async () => {
|
||||
const envelope = await io.runTask(`create-document-${rowIndex}`, async () => {
|
||||
return await createDocumentFromTemplate({
|
||||
templateId: template.id,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: template.id,
|
||||
},
|
||||
userId,
|
||||
teamId,
|
||||
recipients: recipients.map((recipient, index) => {
|
||||
@ -124,7 +127,10 @@ export const run = async ({
|
||||
if (sendImmediately) {
|
||||
await io.runTask(`send-document-${rowIndex}`, async () => {
|
||||
await sendDocument({
|
||||
documentId: document.id,
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelope.id,
|
||||
},
|
||||
userId,
|
||||
teamId,
|
||||
requestMetadata: {
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { DocumentStatus, RecipientRole, SigningStatus, WebhookTriggerEvents } from '@prisma/client';
|
||||
import {
|
||||
DocumentStatus,
|
||||
EnvelopeType,
|
||||
RecipientRole,
|
||||
SigningStatus,
|
||||
WebhookTriggerEvents,
|
||||
} from '@prisma/client';
|
||||
import { nanoid } from 'nanoid';
|
||||
import path from 'node:path';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
@ -22,7 +28,7 @@ import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-we
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
|
||||
import {
|
||||
ZWebhookDocumentSchema,
|
||||
mapDocumentToWebhookDocumentPayload,
|
||||
mapEnvelopeToWebhookDocumentPayload,
|
||||
} from '../../../types/webhook-payload';
|
||||
import { prefixedId } from '../../../universal/id';
|
||||
import { getFileServerSide } from '../../../universal/upload/get-file.server';
|
||||
@ -30,6 +36,7 @@ import { putPdfFileServerSide } from '../../../universal/upload/put-file.server'
|
||||
import { fieldsContainUnsignedRequiredField } from '../../../utils/advanced-fields-helpers';
|
||||
import { isDocumentCompleted } from '../../../utils/document';
|
||||
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
|
||||
import { mapDocumentIdToSecondaryId } from '../../../utils/envelope';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSealDocumentJobDefinition } from './seal-document';
|
||||
|
||||
@ -42,24 +49,35 @@ export const run = async ({
|
||||
}) => {
|
||||
const { documentId, sendEmail = true, isResealing = false, requestMetadata } = payload;
|
||||
|
||||
const document = await prisma.document.findFirstOrThrow({
|
||||
const envelope = await prisma.envelope.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
secondaryId: mapDocumentIdToSecondaryId(documentId),
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
recipients: true,
|
||||
envelopeItems: {
|
||||
select: {
|
||||
documentDataId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Todo: Envelopes
|
||||
if (envelope.envelopeItems.length !== 1) {
|
||||
throw new Error('1 Envelope item required');
|
||||
}
|
||||
|
||||
const settings = await getTeamSettings({
|
||||
userId: document.userId,
|
||||
teamId: document.teamId,
|
||||
userId: envelope.userId,
|
||||
teamId: envelope.teamId,
|
||||
});
|
||||
|
||||
const isComplete =
|
||||
document.recipients.some((recipient) => recipient.signingStatus === SigningStatus.REJECTED) ||
|
||||
document.recipients.every((recipient) => recipient.signingStatus === SigningStatus.SIGNED);
|
||||
envelope.recipients.some((recipient) => recipient.signingStatus === SigningStatus.REJECTED) ||
|
||||
envelope.recipients.every((recipient) => recipient.signingStatus === SigningStatus.SIGNED);
|
||||
|
||||
if (!isComplete) {
|
||||
throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
|
||||
@ -71,13 +89,13 @@ export const run = async ({
|
||||
// after it has already run through the update task further below.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const documentStatus = await io.runTask('get-document-status', async () => {
|
||||
return document.status;
|
||||
return envelope.status;
|
||||
});
|
||||
|
||||
// This is the same case as above.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const documentDataId = await io.runTask('get-document-data-id', async () => {
|
||||
return document.documentDataId;
|
||||
return envelope.envelopeItems[0].documentDataId;
|
||||
});
|
||||
|
||||
const documentData = await prisma.documentData.findFirst({
|
||||
@ -87,12 +105,12 @@ export const run = async ({
|
||||
});
|
||||
|
||||
if (!documentData) {
|
||||
throw new Error(`Document ${document.id} has no document data`);
|
||||
throw new Error(`Document ${envelope.id} has no document data`);
|
||||
}
|
||||
|
||||
const recipients = await prisma.recipient.findMany({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
envelopeId: envelope.id,
|
||||
role: {
|
||||
not: RecipientRole.CC,
|
||||
},
|
||||
@ -111,7 +129,7 @@ export const run = async ({
|
||||
|
||||
const fields = await prisma.field.findMany({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
envelopeId: envelope.id,
|
||||
},
|
||||
include: {
|
||||
signature: true,
|
||||
@ -120,7 +138,7 @@ export const run = async ({
|
||||
|
||||
// Skip the field check if the document is rejected
|
||||
if (!isRejected && fieldsContainUnsignedRequiredField(fields)) {
|
||||
throw new Error(`Document ${document.id} has unsigned required fields`);
|
||||
throw new Error(`Document ${envelope.id} has unsigned required fields`);
|
||||
}
|
||||
|
||||
if (isResealing) {
|
||||
@ -129,10 +147,10 @@ export const run = async ({
|
||||
documentData.data = documentData.initialData;
|
||||
}
|
||||
|
||||
if (!document.qrToken) {
|
||||
await prisma.document.update({
|
||||
if (!envelope.qrToken) {
|
||||
await prisma.envelope.update({
|
||||
where: {
|
||||
id: document.id,
|
||||
id: envelope.id,
|
||||
},
|
||||
data: {
|
||||
qrToken: prefixedId('qr'),
|
||||
@ -145,7 +163,7 @@ export const run = async ({
|
||||
const certificateData = settings.includeSigningCertificate
|
||||
? await getCertificatePdf({
|
||||
documentId,
|
||||
language: document.documentMeta?.language,
|
||||
language: envelope.documentMeta?.language,
|
||||
}).catch((e) => {
|
||||
console.log('Failed to get certificate PDF');
|
||||
console.error(e);
|
||||
@ -157,7 +175,7 @@ export const run = async ({
|
||||
const auditLogData = settings.includeAuditLog
|
||||
? await getAuditLogsPdf({
|
||||
documentId,
|
||||
language: document.documentMeta?.language,
|
||||
language: envelope.documentMeta?.language,
|
||||
}).catch((e) => {
|
||||
console.log('Failed to get audit logs PDF');
|
||||
console.error(e);
|
||||
@ -204,7 +222,7 @@ export const run = async ({
|
||||
|
||||
for (const field of fields) {
|
||||
if (field.inserted) {
|
||||
document.useLegacyFieldInsertion
|
||||
envelope.useLegacyFieldInsertion
|
||||
? await legacy_insertFieldInPDF(pdfDoc, field)
|
||||
: await insertFieldInPDF(pdfDoc, field);
|
||||
}
|
||||
@ -217,7 +235,8 @@ export const run = async ({
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
const pdfBuffer = await signPdf({ pdf: Buffer.from(pdfBytes) });
|
||||
|
||||
const { name } = path.parse(document.title);
|
||||
// Todo: Envelopes - Use the envelope item title instead.
|
||||
const { name } = path.parse(envelope.title);
|
||||
|
||||
// Add suffix based on document status
|
||||
const suffix = isRejected ? '_rejected.pdf' : '_signed.pdf';
|
||||
@ -238,7 +257,7 @@ export const run = async ({
|
||||
distinctId: nanoid(),
|
||||
event: 'App: Document Sealed',
|
||||
properties: {
|
||||
documentId: document.id,
|
||||
documentId: envelope.id,
|
||||
isRejected,
|
||||
},
|
||||
});
|
||||
@ -252,9 +271,9 @@ export const run = async ({
|
||||
},
|
||||
});
|
||||
|
||||
await tx.document.update({
|
||||
await tx.envelope.update({
|
||||
where: {
|
||||
id: document.id,
|
||||
id: envelope.id,
|
||||
},
|
||||
data: {
|
||||
status: isRejected ? DocumentStatus.REJECTED : DocumentStatus.COMPLETED,
|
||||
@ -274,7 +293,7 @@ export const run = async ({
|
||||
await tx.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
|
||||
documentId: document.id,
|
||||
envelopeId: envelope.id,
|
||||
requestMetadata,
|
||||
user: null,
|
||||
data: {
|
||||
@ -289,21 +308,23 @@ export const run = async ({
|
||||
await io.runTask('send-completed-email', async () => {
|
||||
let shouldSendCompletedEmail = sendEmail && !isResealing && !isRejected;
|
||||
|
||||
if (isResealing && !isDocumentCompleted(document.status)) {
|
||||
if (isResealing && !isDocumentCompleted(envelope.status)) {
|
||||
shouldSendCompletedEmail = sendEmail;
|
||||
}
|
||||
|
||||
if (shouldSendCompletedEmail) {
|
||||
await sendCompletedEmail({ documentId, requestMetadata });
|
||||
await sendCompletedEmail({
|
||||
id: { type: 'envelopeId', id: envelope.id },
|
||||
requestMetadata,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const updatedDocument = await prisma.document.findFirstOrThrow({
|
||||
const updatedEnvelope = await prisma.envelope.findFirstOrThrow({
|
||||
where: {
|
||||
id: document.id,
|
||||
id: envelope.id,
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
documentMeta: true,
|
||||
recipients: true,
|
||||
},
|
||||
@ -313,8 +334,8 @@ export const run = async ({
|
||||
event: isRejected
|
||||
? WebhookTriggerEvents.DOCUMENT_REJECTED
|
||||
: WebhookTriggerEvents.DOCUMENT_COMPLETED,
|
||||
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
|
||||
userId: updatedDocument.userId,
|
||||
teamId: updatedDocument.teamId ?? undefined,
|
||||
data: ZWebhookDocumentSchema.parse(mapEnvelopeToWebhookDocumentPayload(updatedEnvelope)),
|
||||
userId: updatedEnvelope.userId,
|
||||
teamId: updatedEnvelope.teamId ?? undefined,
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user