feat(documenso): version-aware field placement + void abstractions

Adds DOCUMENSO_API_VERSION env (default v1) plus per-port override.
Introduces placeFields, placeDefaultSignatureFields, and voidDocument
that hide v1 (per-field POST, pixel coords) vs v2 (bulk POST, percent +
fieldMeta) differences. cancelDocument now voids in Documenso first and
treats transient void failures as recoverable so the CRM stays the
system of record. 16 unit specs cover dispatch, layout math, idempotent
404, and v1 pixel conversion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-04-28 02:22:04 +02:00
parent af2db06244
commit da44e8ecbe
5 changed files with 551 additions and 5 deletions

View File

@@ -24,6 +24,7 @@ import {
createDocument as documensoCreate,
sendDocument as documensoSend,
downloadSignedPdf,
voidDocument as documensoVoid,
} from '@/lib/services/documenso-client';
import type {
CreateDocumentInput,
@@ -832,7 +833,20 @@ export async function cancelDocument(
throw new ConflictError(`Document is already ${existing.status}`);
}
// PR2 will wire the Documenso void here.
// CRM is the system of record for cancellation status. A transient
// Documenso failure shouldn't block the user from marking the doc cancelled
// here — voidDocument already treats 404 as success, and the periodic
// webhook receiver will reconcile if the remote void eventually lands.
if (existing.documensoId) {
try {
await documensoVoid(existing.documensoId, portId);
} catch (err) {
logger.warn(
{ err, documentId, documensoId: existing.documensoId },
'Documenso void failed; cancelling locally anyway',
);
}
}
const [updated] = await db
.update(documents)