/** * POST /api/offers/contract/send * Body: contract payload incl. email { to, cc?, subject, message }. * Builds the branded Fulfillment-Rahmenvertrag PDF ONCE, emails it to the * recipient (from info@logyou.de) and attaches the same PDF to the triggering * record: partner → C_BPartner, lead → Lead_User. */ import nodemailer from 'nodemailer' import refreshTokenHelper from '../../../utils/refreshTokenHelper' import errorHandlingHelper from '../../../utils/errorHandlingHelper' import forceLogoutHelper from '../../../utils/forceLogoutHelper' import { BRAND, LEGAL_FOOTER_LINES } from '../../../utils/offers/quotePdf' import { generateContractPdf } from '../../../utils/offers/contractPdf' import { attachToStrapi } from '../../../utils/inbox/strapiAttach' const EMAIL_TEXTS: any = { de: { headline: 'Ihr Fulfillment-Vertrag', attachmentBox: 'Der Vertrag liegt dieser E-Mail als PDF-Anhang bei. Bitte senden Sie uns ein unterschriebenes Exemplar zurück.', auto: 'Diese E-Mail wurde automatisch versendet.' }, en: { headline: 'Your fulfillment agreement', attachmentBox: 'The agreement is attached to this email as a PDF. Please return a signed copy to us.', auto: 'This email was sent automatically.' } } const generateContractEmailHtml = (message: string, language: string) => { const T = EMAIL_TEXTS[language === 'en' ? 'en' : 'de'] const messageHtml = String(message || '') .replace(/&/g, '&').replace(//g, '>') .replace(/\n/g, '
') return `
${T.headline}
LogYou GmbH · logyou.de
${messageHtml}
📄  ${T.attachmentBox}
${LEGAL_FOOTER_LINES.join('
')}

${T.auto}
` } const handleFunc = async (event: any, authToken: string | null = null) => { const token = authToken ?? await getTokenHelper(event) if (!token) return { status: 401, message: 'Not authenticated' } const body = await readBody(event) if (!body?.customer?.company || !body?.customer?.contactName) { return { status: 400, message: 'Company and contact name are required' } } const toEmail = String(body?.email?.to || '').trim() if (!/.+@.+\..+/.test(toEmail)) { return { status: 400, message: 'A valid recipient email is required' } } const language = body?.meta?.language === 'en' ? 'en' : 'de' // Build the PDF once — reused for the email attachment AND the record attachment. const { buffer, filename } = await generateContractPdf(body) const transporter = nodemailer.createTransport({ host: 'localhost', port: 25, secure: false, tls: { rejectUnauthorized: false } }) const ccList = String(body?.email?.cc || '') .split(/[,;]/) .map((s: string) => s.trim()) .filter((s: string) => s.length > 0) try { await transporter.sendMail({ from: 'info@logyou.de', to: toEmail, cc: ccList.length > 0 ? ccList : undefined, subject: body?.email?.subject || filename.replace(/\.pdf$/, ''), text: body?.email?.message || '', html: generateContractEmailHtml(body?.email?.message || '', language), attachments: [ { filename, content: buffer, contentType: 'application/pdf' } ] }) } catch (err: any) { console.error('Contract email send error:', err) return { status: 500, message: `Failed to send email: ${err.message}` } } // Attach the PDF to the triggering record — its own try/catch so an // attachment failure never reverts the successful send. let attached = false let attachWarning = '' try { const tableName = body.source === 'lead' ? 'Lead_User' : 'C_BPartner' const recordId = Number(body.recordId) if (recordId > 0) { await attachToStrapi(event, token, { tableName, recordId, recordUu: body.recordUu || undefined, buffer, filename, mimeType: 'application/pdf' }) attached = true } else { attachWarning = 'No record id — attachment skipped' } } catch (err: any) { console.error('Contract attachment error:', err) attachWarning = 'Email sent, but attaching the PDF to the record failed' } return { status: 200, message: 'Contract sent successfully', filename, attached, warning: attachWarning || undefined } } export default defineEventHandler(async (event) => { let data: any = {} try { data = await handleFunc(event) } catch (err: any) { try { let authToken: any = await refreshTokenHelper(event) data = await handleFunc(event, authToken) } catch (error: any) { data = errorHandlingHelper(err?.data ?? err, error?.data ?? error) forceLogoutHelper(event, data) } } return data })