From b55c419074bed1a671092e75a3384b12a009c626 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Wed, 16 Oct 2024 19:03:14 +0000 Subject: [PATCH] chore: minor changes --- .../documents/[id]/edit-document.tsx | 1 - .../document/self-sign-document.ts | 149 ++++++++++++++++++ .../trpc/server/document-router/router.ts | 7 +- .../trpc/server/document-router/schema.ts | 1 - .../primitives/document-flow/add-fields.tsx | 9 +- 5 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 packages/lib/server-only/document/self-sign-document.ts diff --git a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx index e8ddb013c..40a028022 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx @@ -287,7 +287,6 @@ export const EditDocumentForm = ({ if (hasSameOwnerAsRecipient) { await selfSignDocument({ documentId: document.id, - teamId: team?.id, }); router.push(`/sign/${recipients[0].token}`); diff --git a/packages/lib/server-only/document/self-sign-document.ts b/packages/lib/server-only/document/self-sign-document.ts new file mode 100644 index 000000000..d4bcb6d93 --- /dev/null +++ b/packages/lib/server-only/document/self-sign-document.ts @@ -0,0 +1,149 @@ +import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; +import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; +import { putPdfFile } from '@documenso/lib/universal/upload/put-file'; +import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; +import { prisma } from '@documenso/prisma'; +import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client'; + +import { jobs } from '../../jobs/client'; +import { getFile } from '../../universal/upload/get-file'; +import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf'; + +export type SelfSignDocumentOptions = { + documentId: number; + userId: number; + requestMetadata?: RequestMetadata; +}; + +export const selfSignDocument = async ({ + documentId, + userId, + requestMetadata, +}: SelfSignDocumentOptions) => { + const user = await prisma.user.findFirstOrThrow({ + where: { + id: userId, + }, + select: { + id: true, + name: true, + email: true, + }, + }); + + const document = await prisma.document.findUnique({ + where: { + id: documentId, + userId, + teamId: null, + }, + include: { + Recipient: { + orderBy: [{ signingOrder: { sort: 'asc', nulls: 'last' } }, { id: 'asc' }], + }, + documentMeta: true, + documentData: true, + }, + }); + + if (!document) { + throw new Error('Document not found'); + } + + if (document.Recipient.length === 0) { + throw new Error('Document has no recipients'); + } + + if (document.Recipient.length !== 1 || document.Recipient[0].email !== user.email) { + throw new Error('Invalid document for self-signing'); + } + + if (document.status === DocumentStatus.COMPLETED) { + throw new Error('Can not sign completed document'); + } + + const { documentData } = document; + + if (!documentData.data) { + throw new Error('Document data not found'); + } + + if (document.formValues) { + const file = await getFile(documentData); + + const prefilled = await insertFormValuesInPdf({ + pdf: Buffer.from(file), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + formValues: document.formValues as Record, + }); + + const newDocumentData = await putPdfFile({ + name: document.title, + type: 'application/pdf', + arrayBuffer: async () => Promise.resolve(prefilled), + }); + + const result = await prisma.document.update({ + where: { + id: document.id, + }, + data: { + documentDataId: newDocumentData.id, + }, + }); + + Object.assign(document, result); + } + + const recipientHasNoActionToTake = + document.Recipient[0].role === RecipientRole.CC || + document.Recipient[0].signingStatus === SigningStatus.SIGNED; + + if (recipientHasNoActionToTake) { + await jobs.triggerJob({ + name: 'internal.seal-document', + payload: { + documentId, + requestMetadata, + }, + }); + + return await prisma.document.findFirstOrThrow({ + where: { + id: documentId, + }, + include: { + Recipient: true, + }, + }); + } + + const updatedDocument = await prisma.$transaction(async (tx) => { + if (document.status === DocumentStatus.DRAFT) { + await tx.documentAuditLog.create({ + data: createDocumentAuditLogData({ + // todo: use a different audit log type + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT, + documentId: document.id, + requestMetadata, + user, + data: {}, + }), + }); + } + + return await tx.document.update({ + where: { + id: documentId, + }, + data: { + status: DocumentStatus.PENDING, + }, + include: { + Recipient: true, + }, + }); + }); + + return updatedDocument; +}; diff --git a/packages/trpc/server/document-router/router.ts b/packages/trpc/server/document-router/router.ts index 5db61ca87..58c79cf43 100644 --- a/packages/trpc/server/document-router/router.ts +++ b/packages/trpc/server/document-router/router.ts @@ -17,6 +17,7 @@ import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/ import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team'; import { resendDocument } from '@documenso/lib/server-only/document/resend-document'; import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword'; +import { selfSignDocument } from '@documenso/lib/server-only/document/self-sign-document'; import { sendDocument } from '@documenso/lib/server-only/document/send-document'; import { updateDocumentSettings } from '@documenso/lib/server-only/document/update-document-settings'; import { updateTitle } from '@documenso/lib/server-only/document/update-title'; @@ -372,13 +373,11 @@ export const documentRouter = router({ .input(ZSelfSignDocumentMutationSchema) .mutation(async ({ input, ctx }) => { try { - const { documentId, teamId } = input; + const { documentId } = input; - return await sendDocument({ + return await selfSignDocument({ userId: ctx.user.id, documentId, - teamId, - sendEmail: false, requestMetadata: extractNextApiRequestMetadata(ctx.req), }); } catch (err) { diff --git a/packages/trpc/server/document-router/schema.ts b/packages/trpc/server/document-router/schema.ts index 41b757f81..dfd9c34aa 100644 --- a/packages/trpc/server/document-router/schema.ts +++ b/packages/trpc/server/document-router/schema.ts @@ -142,7 +142,6 @@ export const ZSendDocumentMutationSchema = z.object({ export const ZSelfSignDocumentMutationSchema = z.object({ documentId: z.number(), - teamId: z.number().optional(), }); export const ZSetPasswordForDocumentMutationSchema = z.object({ diff --git a/packages/ui/primitives/document-flow/add-fields.tsx b/packages/ui/primitives/document-flow/add-fields.tsx index 04afdf617..071adaa3d 100644 --- a/packages/ui/primitives/document-flow/add-fields.tsx +++ b/packages/ui/primitives/document-flow/add-fields.tsx @@ -100,13 +100,6 @@ export type AddFieldsFormProps = { teamId?: number; }; -// Self Sign -// If the recipient is the user and it is the only recipient, -// dont send the document, after adding the fields, skip the next step and go straight to the point -// no need for the sending section -// it will create the document -// and redirect you to the signing page. - export const AddFieldsFormPartial = ({ documentFlow, hideRecipients = false, @@ -1080,7 +1073,7 @@ export const AddFieldsFormPartial = ({ documentFlow.onBackStep?.(); }} goBackLabel={canRenderBackButtonAsRemove ? msg`Remove` : undefined} - goNextLabel={hasSameOwnerAsRecipient ? msg`Self Sign` : undefined} + goNextLabel={hasSameOwnerAsRecipient ? msg`Sign` : undefined} onGoNextClick={handleGoNextClick} />