/** * GET /api/offers/contract/signing-status?source=lead|partner&recordId=N * Digital-signing (kind 'contract') AND online-confirmation (kind 'quote') * requests of a record: `latest` = newest contract link (chip/modal), * `latestQuote` = newest quote-confirmation link, `history` = every link of * both kinds (newest first) for the record page's link panel — each with * state, expiry, remaining days, signer and documents. */ import { getLatestSigningRequestForRecord, listSigningRequestsForRecord, effectiveStatus, isSigningAvailable } from '../../../utils/offers/contractSigningDb' import { sepaLink, sepaDocumentLink } from '../../../utils/offers/sepaMandate' import { signingLink } from '../../../utils/offers/contractSigning' import { confirmLink, confirmDocumentLink } from '../../../utils/offers/quoteConfirmation' import { APP_BASE_URL } from '../../../utils/emailVerification' export default defineEventHandler(async (event) => { const token = await getTokenHelper(event) if (!token) return { status: 401, message: 'Not authenticated' } const q = getQuery(event) const source = q.source === 'lead' ? 'lead' : 'partner' const recordId = Number(q.recordId) if (!(recordId > 0)) return { status: 400, message: 'recordId required' } if (!isSigningAvailable()) return { status: 200, available: false, latest: null, latestQuote: null, history: [] } const toView = (r: any) => ({ token: r.token, kind: r.kind === 'quote' ? 'quote' : r.kind === 'sepa' ? 'sepa' : 'contract', link: r.kind === 'quote' ? confirmLink(r.token) : r.kind === 'sepa' ? sepaLink(r.token) : signingLink(r.token), state: effectiveStatus(r), daysLeft: effectiveStatus(r) === 'pending' ? Math.max(0, Math.ceil((new Date(r.expiresAt).getTime() - Date.now()) / 86400000)) : null, createdAt: r.createdAt, expiresAt: r.expiresAt, signedAt: r.signedAt, lastOpenedAt: r.lastOpenedAt, renewalRequestedAt: r.renewalRequestedAt, email: r.email, company: r.company, contactName: r.contactName, language: r.language, documents: (r.status === 'signed' ? r.signedDocuments : r.documents).map((d: any) => ({ key: d.key, title: d.title, filename: d.filename, version: d.version, sha256: d.sha256, signed: !!d.signed, // Portal copy — only served while the link is pending/signed (404/410 otherwise). url: r.kind === 'quote' ? confirmDocumentLink(r.token, d.key) : r.kind === 'sepa' ? sepaDocumentLink(r.token, d.key) : `${APP_BASE_URL}/api/public/sign/${r.token}/document/${d.key}` })), signer: r.signer ? { name: r.signer.contactName, company: r.signer.company, email: r.signer.email, position: r.signer.position, ustId: r.signer.ustId, signedAt: r.signer.signedAt || r.signer.confirmedAt, acceptances: r.signer.acceptances || [] } : null }) const latest = getLatestSigningRequestForRecord(source, recordId, 'contract') const latestQuote = getLatestSigningRequestForRecord(source, recordId, 'quote') return { status: 200, available: true, latest: latest ? toView(latest) : null, latestQuote: latestQuote ? toView(latestQuote) : null, history: listSigningRequestsForRecord(source, recordId, 50).map(toView) } })