import { getInterestById, updateInterest } from '~/server/utils/nocodb'; // Helper function to create embedded signing URLs const createEmbeddedSigningUrl = (documensoUrl: string, signerType: 'client' | 'cc' | 'developer'): string => { if (!documensoUrl) return ''; const token = documensoUrl.split('/').pop(); return `https://portnimara.com/sign/${signerType}/${token}`; }; interface DocumensoRecipient { id: number; name: string; email: string; role: 'SIGNER' | 'APPROVER'; signingOrder: number; signingUrl?: string; } interface DocumensoResponse { documentId: number; recipients: Array<{ recipientId: number; name: string; email: string; token: string; role: 'SIGNER' | 'APPROVER'; signingOrder: number; signingUrl: string; }>; } export default defineEventHandler(async (event) => { const xTagHeader = getRequestHeader(event, "x-tag"); if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); } try { const body = await readBody(event); const { interestId } = body; if (!interestId) { throw createError({ statusCode: 400, statusMessage: "Interest ID is required" }); } // Get the interest data const interest = await getInterestById(interestId); if (!interest) { throw createError({ statusCode: 404, statusMessage: "Interest not found" }); } // Documenso API configuration - moved to top for use throughout const documensoApiKey = process.env.NUXT_DOCUMENSO_API_KEY; const documensoBaseUrl = process.env.NUXT_DOCUMENSO_BASE_URL; const templateId = '9'; if (!documensoApiKey || !documensoBaseUrl) { throw createError({ statusCode: 500, statusMessage: "Documenso configuration missing. Please check NUXT_DOCUMENSO_API_KEY and NUXT_DOCUMENSO_BASE_URL environment variables." }); } // Check if uploaded EOI documents exist - prevent generation if they do const eoiDocuments = interest['EOI Document'] || []; if (eoiDocuments.length > 0) { throw createError({ statusCode: 400, statusMessage: "Cannot generate EOI - uploaded documents already exist. Please remove uploaded documents first." }); } // Check if EOI already exists (has signature links) if (interest['Signature Link Client'] && interest['Signature Link CC'] && interest['Signature Link Developer']) { console.log('EOI already exists, returning existing links'); return { success: true, documentId: 'existing', clientSigningUrl: interest['Signature Link Client'], signingLinks: { 'Client': interest['Signature Link Client'], 'CC': interest['Signature Link CC'], 'Developer': interest['Signature Link Developer'] } }; } // If there's an existing generated document, delete it from Documenso first if (interest['documensoID']) { console.log('Existing generated document found, deleting from Documenso first'); try { const deleteResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${interest['documensoID']}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${documensoApiKey}`, 'Content-Type': 'application/json' } }); if (deleteResponse.ok) { console.log('Successfully deleted old document from Documenso'); } else { console.warn('Failed to delete old document from Documenso, continuing with new generation'); } } catch (error) { console.warn('Error deleting old document from Documenso:', error); // Continue with generation even if deletion fails } } // Validate required fields const requiredFields = [ { field: 'Full Name', value: interest['Full Name'] }, { field: 'Email Address', value: interest['Email Address'] }, { field: 'Yacht Name', value: interest['Yacht Name'] }, { field: 'Length', value: interest['Length'] }, { field: 'Width', value: interest['Width'] }, { field: 'Depth', value: interest['Depth'] } ]; // Address is optional - use a default if not provided const address = interest['Address'] || 'Not Provided'; const missingFields = requiredFields.filter(f => !f.value).map(f => f.field); if (missingFields.length > 0) { throw createError({ statusCode: 400, statusMessage: `Please fill in the following required fields before generating EOI: ${missingFields.join(', ')}. You can update these fields in the interest details form.` }); } // Get linked berths const berthsResponse = await $fetch<{ list: Array<{ 'Mooring Number': string }> }>( "/api/get-interest-berths", { headers: { "x-tag": xTagHeader, }, params: { interestId: interestId, linkType: "berths", }, } ); const berths = berthsResponse.list || []; if (berths.length === 0) { throw createError({ statusCode: 400, statusMessage: "No berths linked to this interest. Please link at least one berth." }); } // Concatenate berth numbers const berthNumbers = berths.map(b => b['Mooring Number']).join(', '); // 1. Get template (optional - just to verify it exists) try { const templateResponse = await fetch(`${documensoBaseUrl}/api/v1/templates/${templateId}`, { method: 'GET', headers: { 'Authorization': `Bearer ${documensoApiKey}` } }); if (!templateResponse.ok) { throw new Error(`Template not found: ${templateResponse.statusText}`); } } catch (error) { console.error('Failed to verify template:', error); throw createError({ statusCode: 500, statusMessage: "Failed to verify template. Please check Documenso configuration." }); } // 2. Create document const createDocumentPayload = { meta: { message: `Dear ${interest['Full Name']},\n\nThank you for your interest in a berth at Port Nimara. Please click the link above to sign your LOI.\n\nBest Regards,\nPort Nimara Team`, subject: "Your LOI is ready to be signed", redirectUrl: "https://portnimara.com", distributionMethod: "SEQUENTIAL" }, title: `${interest['Full Name']}-EOI-NDA`, externalId: `loi-${interestId}`, formValues: { "Name": interest['Full Name'], "Draft": interest['Depth'], "Email": interest['Email Address'], "Width": interest['Width'], "Length": interest['Length'], "Address": address, "Lease_10": false, "Purchase": true, "Yacht Name": interest['Yacht Name'], "Berth Number": berthNumbers }, recipients: [ { id: 155, name: interest['Full Name'], role: "SIGNER", email: interest['Email Address'], signingOrder: 1 }, { id: 156, name: "David Mizrahi", role: "SIGNER", email: "dm@portnimara.com", signingOrder: 3 }, { id: 157, name: "Oscar Faragher", role: "APPROVER", email: "sales@portnimara.com", signingOrder: 2 } ] }; let documentResponse: DocumensoResponse; try { const response = await fetch(`${documensoBaseUrl}/api/v1/templates/${templateId}/generate-document`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${documensoApiKey}` }, body: JSON.stringify(createDocumentPayload) }); if (!response.ok) { const errorText = await response.text(); console.error('Failed to create document:', errorText); throw new Error(`Failed to create document: ${response.statusText}`); } documentResponse = await response.json(); } catch (error) { console.error('Document creation error:', error); throw createError({ statusCode: 500, statusMessage: "Failed to create EOI document. Please try again." }); } // 3. Send document (moves from draft to active and sends emails) try { const sendResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${documentResponse.documentId}/send`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${documensoApiKey}` }, body: JSON.stringify({ sendEmail: true, sendCompletionEmails: true }) }); if (!sendResponse.ok) { const errorText = await sendResponse.text(); console.error('Failed to send document:', errorText); throw new Error(`Failed to send document: ${sendResponse.statusText}`); } console.log('Document sent successfully'); } catch (error) { console.error('Document send error:', error); throw createError({ statusCode: 500, statusMessage: "Document created but failed to send. Please check Documenso dashboard." }); } // Extract signing URLs from recipients const signingLinks: Record = {}; if (documentResponse.recipients) { documentResponse.recipients.forEach(recipient => { if (recipient.signingUrl) { if (recipient.email === interest['Email Address']) { signingLinks['Client'] = recipient.signingUrl; } else if (recipient.email === 'dm@portnimara.com') { signingLinks['David Mizrahi'] = recipient.signingUrl; } else if (recipient.email === 'sales@portnimara.com') { signingLinks['Oscar Faragher'] = recipient.signingUrl; } } }); } // 4. Update interest record const currentDate = new Date(); const dateTimeString = currentDate.toLocaleString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }); const extraComments = interest['Extra Comments'] || ''; const updatedComments = extraComments + (extraComments ? '\n\n' : '') + `EOI Generated ${dateTimeString}`; const updateData: any = { 'EOI Status': 'Waiting for Signatures', 'Sales Process Level': 'LOI and NDA Sent', // Don't set EOI Time Sent here - only when email is sent or link is copied 'Extra Comments': updatedComments, 'documensoID': documentResponse.documentId.toString() }; // DEBUG: Log the documensoID being saved console.log('[generate-eoi] DEBUGGING documensoID save:', { interestId: interestId, documentId: documentResponse.documentId, documentId_type: typeof documentResponse.documentId, documensoID_string: documentResponse.documentId.toString(), documensoID_string_type: typeof documentResponse.documentId.toString(), updateData_documensoID: updateData['documensoID'], updateData_documensoID_type: typeof updateData['documensoID'], full_updateData: updateData }); // Add signing links to update data with new column names console.log('[EMBEDDED] Available signing links:', signingLinks); if (signingLinks['Client']) { updateData['Signature Link Client'] = signingLinks['Client']; const embeddedClientUrl = createEmbeddedSigningUrl(signingLinks['Client'], 'client'); updateData['EmbeddedSignatureLinkClient'] = embeddedClientUrl; console.log('[EMBEDDED] Client URL:', signingLinks['Client'], '-> Embedded:', embeddedClientUrl); } if (signingLinks['David Mizrahi']) { updateData['Signature Link Developer'] = signingLinks['David Mizrahi']; const embeddedDevUrl = createEmbeddedSigningUrl(signingLinks['David Mizrahi'], 'developer'); updateData['EmbeddedSignatureLinkDeveloper'] = embeddedDevUrl; console.log('[EMBEDDED] Developer URL:', signingLinks['David Mizrahi'], '-> Embedded:', embeddedDevUrl); } if (signingLinks['Oscar Faragher']) { updateData['Signature Link CC'] = signingLinks['Oscar Faragher']; const embeddedCCUrl = createEmbeddedSigningUrl(signingLinks['Oscar Faragher'], 'cc'); updateData['EmbeddedSignatureLinkCC'] = embeddedCCUrl; console.log('[EMBEDDED] CC URL:', signingLinks['Oscar Faragher'], '-> Embedded:', embeddedCCUrl); } console.log('[EMBEDDED] Final updateData being sent to NocoDB:', updateData); await updateInterest(interestId, updateData); return { success: true, documentId: documentResponse.documentId, clientSigningUrl: signingLinks['Client'] || '', signingLinks: signingLinks }; } catch (error) { console.error('Failed to generate EOI document:', error); if (error instanceof Error) { throw createError({ statusCode: 500, statusMessage: error.message || "Failed to generate EOI document" }); } else { throw createError({ statusCode: 500, statusMessage: "An unexpected error occurred", }); } } });