import { EnvelopeType, SigningStatus } from '@prisma/client'; import { jobs } from '@documenso/lib/jobs/client'; import { prisma } from '@documenso/prisma'; import { AppError, AppErrorCode } from '../../errors/app-error'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import type { EnvelopeIdOptions } from '../../utils/envelope'; import { mapSecondaryIdToDocumentId, unsafeBuildEnvelopeIdQuery } from '../../utils/envelope'; export type RejectDocumentWithTokenOptions = { token: string; id: EnvelopeIdOptions; reason: string; requestMetadata?: RequestMetadata; }; export async function rejectDocumentWithToken({ token, id, reason, requestMetadata, }: RejectDocumentWithTokenOptions) { // Find the recipient and document in a single query const recipient = await prisma.recipient.findFirst({ where: { token, envelope: unsafeBuildEnvelopeIdQuery(id, EnvelopeType.DOCUMENT), }, include: { envelope: true, }, }); const envelope = recipient?.envelope; if (!recipient || !envelope) { throw new AppError(AppErrorCode.NOT_FOUND, { message: 'Document or recipient not found', }); } // Update the recipient status to rejected const [updatedRecipient] = await prisma.$transaction([ prisma.recipient.update({ where: { id: recipient.id, }, data: { signedAt: new Date(), signingStatus: SigningStatus.REJECTED, rejectionReason: reason, }, }), prisma.documentAuditLog.create({ data: createDocumentAuditLogData({ envelopeId: envelope.id, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED, user: { name: recipient.name, email: recipient.email, }, data: { recipientEmail: recipient.email, recipientName: recipient.name, recipientId: recipient.id, recipientRole: recipient.role, reason, }, requestMetadata, }), }), ]); const legacyDocumentId = mapSecondaryIdToDocumentId(envelope.secondaryId); // Trigger the seal document job to process the document asynchronously await jobs.triggerJob({ name: 'internal.seal-document', payload: { documentId: legacyDocumentId, requestMetadata, }, }); // Send email notifications to the rejecting recipient await jobs.triggerJob({ name: 'send.signing.rejected.emails', payload: { recipientId: recipient.id, documentId: legacyDocumentId, }, }); // Send cancellation emails to other recipients await jobs.triggerJob({ name: 'send.document.cancelled.emails', payload: { documentId: legacyDocumentId, cancellationReason: reason, requestMetadata, }, }); return updatedRecipient; }