Files
pn-new-crm/docs/eoi-documenso-field-mapping.md
Matt Ciaccio 312ebf1a88 docs(eoi): document multi-berth Berth Range field + legacy parity
The user asked us to confirm we copied the Documenso template's
auto-fill schema verbatim from the legacy system. Confirmed and
documented in the canonical mapping file:

- Every legacy formValues key (Name, Email, Address, Yacht Name,
  Length, Width, Draft, Berth Number, Lease_10, Purchase) is still
  emitted with identical names and types — single-berth EOIs are
  byte-for-byte compatible with template id 8.
- Phase 5 added one new field: `Berth Range` (compact range string
  for multi-berth EOIs from the is_in_eoi_bundle junction rows).
  Documenso silently drops unknown formValues, so the live template
  will simply not render the range until someone adds the field. The
  doc now flags this explicitly.
- Verified buildDocumensoPayload() populates all 11 fields from the
  resolved EoiContext; tests at tests/unit/services/documenso-payload
  cover every field.

The "rest is handled inside Documenso" (signature, date, terms) -
those fields live on the template itself and don't appear in our
formValues map.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 04:13:32 +02:00

13 KiB

Documenso EOI Template — Field Mapping

Purpose: This doc is the canonical reference for mapping the Documenso EOI template's formValues keys to the new data model's EoiContext shape. It drives buildDocumensoPayload() (Task 11.2), the in-app Standard EOI HTML tokens (Task 11.3), and the Spec 2 importer's yacht/company hydration.

Source

The legacy field list comes from client-portal/server/api/eoi/generate-quick-eoi.ts, specifically the POST body sent to POST /api/v1/templates/{templateId}/generate-document (Documenso template 8). The relevant lines in that file are around the createDocumentPayload.formValues object.

Documenso template formValues keys

Documenso template IDs and recipient IDs are configured via env vars:

  • NUXT_DOCUMENSO_TEMPLATE_ID (default: 8)
  • NUXT_DOCUMENSO_CLIENT_RECIPIENT_ID (default: 192) — signing order 1
  • NUXT_DOCUMENSO_DEVELOPER_RECIPIENT_ID (default: 193) — signing order 2
  • NUXT_DOCUMENSO_APPROVAL_RECIPIENT_ID (default: 194) — APPROVER, signing order 3

The template exposes eight text fields (formValues keys) and two boolean checkboxes.

Field mapping

The legacy template (Documenso template 8, configured in production) auto-fills exactly the fields below. All eight text fields + two booleans are populated by buildDocumensoPayload() from the resolved EoiContext. Anything else on the form (signature, date, terms acknowledgment) is filled in by the client inside Documenso.

Documenso key Type Legacy source New EoiContext path Notes
Name text interest['Full Name'] context.client.fullName The interest's point-of-contact client (billing signer).
Email text interest['Email Address'] context.client.primaryEmail Primary email contact from client_contacts.
Address text interest['Address'] concat context.client.address.{street,city,country} Concatenate street, city, country with ', '. Empty if address is null.
Yacht Name text interest['Yacht Name'] context.yacht.name Yacht is now a first-class row; pulled via interest.yachtId. Empty string when no yacht is linked yet.
Length text interest['Length'] context.yacht.lengthFt Boat dimension. Send as string. Documenso doesn't enforce numeric format. Empty string when not applicable.
Width text interest['Width'] context.yacht.widthFt Same.
Draft text interest['Depth'] context.yacht.draftFt Legacy field was named "Depth" in NocoDB; Documenso key is "Draft".
Berth Number text berthNumbers (joined) context.berth.mooringNumber The interest's PRIMARY berth (resolved via interest_berths.is_primary=true). Empty string when no primary set.
Berth Range text (new) context.eoiBerthRange NEW IN PHASE 5 — compact range string for multi-berth EOIs (e.g. "A1-A3, B5-B7") covering every junction row marked is_in_eoi_bundle=true. Empty string when the bundle is empty. The live Documenso template (id 8) does NOT yet have this field. Add a Berth Range text field to the template before multi-berth EOIs render the range; until then Documenso silently drops the value and only Berth Number (the primary mooring) renders.
Lease_10 boolean hardcoded false false Hardcoded — legacy flow defaults to Purchase (not Lease).
Purchase boolean hardcoded true true Hardcoded — legacy flow defaults to Purchase.

Backwards-compatibility guarantee: every legacy formValues key is still emitted with the same name and type. The only addition is Berth Range (Phase 5). Documenso silently ignores unknown formValues keys, so old templates that don't have Berth Range will simply not render it — single-berth EOIs continue to work identically. No template changes are required for legacy use.

Document meta fields (non-formValues)

Documenso key Type Legacy source New source
meta.message text Dear ${interest['Full Name']}... Dear ${context.client.fullName}, ...port name interpolated
meta.subject text "Your LOI is ready to be signed" Same — constant.
meta.redirectUrl text "https://portnimara.com" context.port.redirectUrl if per-port; otherwise global app URL.
meta.distributionMethod text "NONE" Same — constant. We use manual send flow (Documenso webhook).
title text `${interest['Full Name']}-EOI-NDA` `${context.client.fullName}-EOI-NDA`
externalId text `loi-${interestId}` Same.

Recipients (non-formValues)

Recipient Role Name Email Signing order
Client (signer) SIGNER context.client.fullName context.client.primaryEmail 1
Developer (signer) SIGNER "David Mizrahi" "dm@portnimara.com" 2
Approval (approver) APPROVER "Abbie May" "sales@portnimara.com" 3

The Developer and Approval recipients are currently hardcoded in the legacy flow. In the new system these should eventually come from port-level settings (e.g., ports.settings.eoi.developerName + email). For Task 11.2, keep them hardcoded as the legacy system does — tracking as TODO: "Replace hardcoded Developer/Approval recipients with port-level configuration."

Company-owned yacht handling

The legacy flow has no concept of company ownership — the signer is always the interest's client. In the new system:

  • If context.yacht.ownerType === 'client': behavior unchanged.
  • If context.yacht.ownerType === 'company': the interest's point-of-contact client still signs (they're the representative of the yacht's owning company), but an extra block should appear in the message body: "On behalf of ${context.company.legalName ?? context.company.name} (representing the yacht's owner).". This isn't a separate Documenso field — it's woven into meta.message.

Tracking this in the mapping doc rather than as a hard TODO because company-owned EOIs were rare in the legacy system and need product input before committing to the final wording.

Deprecated fields (no longer sourced from clients)

The legacy system read these fields from the client row. They are now sourced elsewhere:

Legacy source New source
client.yachtName yachts.name via interest.yachtId
client.yachtLengthFt yachts.lengthFt via interest.yachtId
client.yachtWidthFt yachts.widthFt via interest.yachtId
client.yachtDraftFt yachts.draftFt via interest.yachtId
client.companyName companies.name via polymorphic owner resolution
client.berthSizeDesired Removed. Berth is picked via reservation, not text.