DocGen & eSign

DocGen & eSign - Document Template Creation Module

Overview

An admin creates a Document Template, uploads a PDF or Word file, maps each form field in that file to a Salesforce field, and defines who signs it. Nothing here generates or sends a document — it produces the configuration records the runtime engine later consumes.

Three records hold that configuration:

Object

Holds

docgen_esign__Document_Template__c

Template header: source object, file reference, eSign/OTP/reminder/vaulting settings, status

docgen_esign__Document_Data_Mapping__c

One row per document form field → Salesforce field binding

docgen_esign__Document_Signer_Mapping__c

One row per signer: sequence, name/email field paths, SOQL query, assigned signature fields

Supporting metadata:

Type

Purpose

docgen_esign__Child_Object_Mapping_Configurations__mdt

Named child-object mappings for PDF templates; its Document_Template__c field is a semicolon-delimited list of template Ids

Features_Entitlement__c

Org-level licence flags: Eoriginal__c, Digital_Signature__c


Architecture

  ┌───────────────────────────────────────────────────────────────────┐
  │                   Lightning App / Record Page                      │
  │                                                                    │
  │  createDocumentTemplate  ──►  documentTemplateDetail                │
  │      (4-step wizard)              (edit + activate)                 │
  │                                        │                            │
  │                             cloneDocumentTemplate (quick action)    │
  │                                        │                            │
  │              documentTemplateMapping (mapping workspace)            │
  │              ┌──────────────────┬───────────────────────────────┐  │
  │              │ VF iframe        │ Tab: Mapping                  │  │
  │              │ (PSPDFKit        │   └ documentMappingField (×N) │  │
  │              │  viewer)         │ Tab: Signers                  │  │
  │              │   ▲   │postMessage│   └ documentSigners           │  │
  │              │   │   ▼          │       └ documentSignerIndividual│ │
  │              └──────────────────┴──────────└ documentSignerConfiguration
  └───────────────────────────────────────────────────────────────────┘
             │                    │                        │
             ▼                    ▼                        ▼
    PSPDFKitController   AdminConsoleController   DocumentMappingScreenController
             │                    │                        │
             └────────────────────┴────────────────────────┘
                                  ▼
                  Data (DML wrapper) · UtilityClass · DescribeCache
                                  ▼
                 DocumentTemplateTriggerHandler (before insert/update)

Key mechanism — the iframe bridge. The document is rendered by PSPDFKit inside a Visualforce page loaded in an <iframe>. LWC and VF cannot call each other directly, so they communicate via window.postMessage. The LWC listens; the VF page posts. See §7.


Apex — AdminConsoleController

public with sharing. Every SOQL/DML call goes through the Data wrapper, passing enforceSharing (from UtilityClass.getSharingSettings()) for the sharing / FLS / CRUD flags. Most write methods return a JSON-serialised UtilityClass.StatusWrapper (isSuccessful, message, recordId, recordDetails) — the caller must JSON.parse the result.

3.1 Read methods

Method

Returns

What it does

getDocumentTemplates()

List<Document_Template__c>

All templates with the full settings field set, ordered SystemModstamp DESC. Powers the admin console list.

getEmailTemplates(String primaryObjName)

List<EmailTemplate>

Templates whose RelatedEntityType matches the primary object, plus the three packaged templates in the Docgen_eSign email folder: Secure_Document_OTP_Email, Send_Document_for_eSign, Signed_Document_Completed_Copy. Returns null on blank input.

getAvailableObjects() (cacheable)

Map<String,String>

Global describe, keyed "Label (API_Name)" → API_Name. Filters out any API name containing history, tag, share or feed.

getOrgWideEmails() (cacheable)

List<OrgWideEmailAddress>

Source for the sender-address picklist.

enabledServices() (cacheable)

Map<String,Object>

{ eOriginal, digitalSign } read from Features_Entitlement__c. Drives visibility of the Advanced Configurations section.

getTemplateName(Id templateId)

String

Export_Title__c of one template. Swallows exceptions and returns ''. Used to prefill the clone dialog.

isSizeWithtinAllowedLimit(String contentDocId)

Boolean

Delegates to UtilityClass. Enforces the upload ceiling (2.5 MB per the UI message).

3.2 Write methods

createTemplate(String sTemplate, Id contentDocumentId)

  1. Rejects blank JSON or blank contentDocumentId.

  2. Queries ContentVersion for the supplied ContentDocumentId.

  3. Deserialises sTemplate into a Document_Template__c.

  4. Force-sets on the new record:

    • Content_Version_Id__c = the queried ContentVersion Id

    • Update_Salesforce_data_on_signing__c = false

    • Signing_Link_Expiry_Days__c = 30

    • Status__c = Active if UtilityClass.activateTemplateOnCreation() else InActive

  5. Inserts the template, then inserts a ContentDocumentLink (ShareType = 'V') joining the file to it.

Warning: step 2 uses lstCv[0] with no bounds check. An empty result throws and surfaces as a generic failure wrapper.

editTemplate(String sTemplate) — deserialise → Data.modify → re-query the full field set → return it in recordDetails so the LWC can refresh without a second round trip. Used both for the full form save and for the Activate/Deactivate toggle, which sends only {Status__c, Id}.

deleteTemplate(Id recordId)Data.remove on a single-element list.

updateContentVersionId(Id templateId, Id contentVersionId) — repoints a template at a new file version after re-upload. Throws AuraHandledException on blank input; it is the only write method here that throws rather than returning a wrapper.

3.3 Cloning

cloneTemplate(Id templateId, Map<String,Object> cloningConfigurations)Id

Config keys, all supplied by cloneDocumentTemplate.js:

Key

Effect

templateName

Becomes Export_Title__c on the clone

cloneDataFieldMappings

Copy Document_Data_Mapping__c children

cloneSignersMappings

Copy Document_Signer_Mapping__c children

copyDocument

Duplicate the attached file

autoActivate

Status__c = Active, else InActive

Flow: query the source template with an explicit ~25-field list → construct a new record copying those fields → Data.create → conditionally call the three private helpers → return the new Id.

Note: Content_Version_Id__c is deliberately not copied during construction. It is set later by cloneDocumentFile, and only when copyDocument is on. A clone created with copyDocument = false therefore has no file and will fail the mapping screen's file check (§4.1).

Private helper

Behaviour

cloneSignerMappings(oldId, newId)

Describes Document_Signer_Mapping__c, builds dynamic SOQL from all isAccessible() fields, clones each with clone(false, true, false, false) (new Id, deep clone, no readonly timestamps, no autonumber), reparents to newId, bulk-inserts.

cloneDataFieldMappings(oldId, newId)

Identical pattern for Document_Data_Mapping__c.

cloneDocumentFile(oldTemplateId, newTemplateId, oldContentVersionId)

Returns early if the source ContentVersion Id is blank. Reads it, inserts a new ContentVersion (creating a new ContentDocument), re-queries to obtain the new ContentDocumentId, inserts a ContentDocumentLink (ShareType='V') to the clone, then stamps Content_Version_Id__c on the clone.

Warning — known defects here: cloneDataFieldMappings builds a creatableFields list that is never used; cloneDocumentFile never uses its oldTemplateId parameter; cloneTemplate has no failure handling, so a partial clone (template created, children not) is possible.


Apex — DocumentMappingScreenController

public with sharing. Same Data wrapper and enforceSharing pattern.

4.1 getScreenConfigurations(String sTemplateId)

The single bootstrap call for the mapping workspace. Returns bSuccess = false plus erroMessage (sic) on any of four validation failures:

Check

Message shown

Blank template Id

"The provided Template Id is null or blank…"

Template not found

same message

No file attached (Content_Version_Id__c resolves to nothing)

"This document template does not have a document file attached…"

No Primary_sObject__c

"The template is missing a primary object…"

On success:

Key

Value

primarySObject

Primary_sObject__c

bDocumentPDF

Is_Document_PDF__c — the master switch between PDF and Word behaviour throughout the UI

dataMappings

Existing Document_Data_Mapping__c records

sObjectFields

List<DropDownOptions> for the primary object

contentVersionId

Used to build the VF iframe URL

childConfigs

PDF: getChildMappingConfig() (custom metadata). Word: UtilityClass.getChildRelations() (real child relationships).

Warning: getTemplateFile() base64-encodes the entire VersionData blob purely to test whether a file exists — and the encoded string is then discarded, never added to the response. On a 2.5 MB file this burns heap for nothing. Replace with an Id-only query.

4.2 Describe and lookup helpers

Method

Notes

getsObjectFields(String sObjectName) (cacheable)

Returns one DropDownOptions per accessible field: label = "Label (API_Name)", value = API name, dataType = Schema.DisplayType, referenceTo = first target object for REFERENCE fields. Uses DescribeCache rather than raw describes. referenceTo is what lets the signer UI drill one level into a lookup.

getAvailableObjects() (cacheable)

List form of the object picker (label/value only). Duplicates AdminConsoleController.getAvailableObjects with a different return shape — different components consume different shapes.

getFieldType(objectName, fieldName) (private)

Describe → DisplayType.name().

isFieldWriteable(objectAPIName, fieldName) (private)

Describe → isUpdateable().

buildError(mapResponse, message) (private)

Sets bSuccess=false + erroMessage.

getExistingMappings(sTemplateId) (private)

Queries the data-mapping children.

4.3 getChildMappingConfig(String templateId)

Queries all Child_Object_Mapping_Configurations__mdt rows, then filters in Apex: splits each row's Document_Template__c on ; and keeps rows whose list contains templateId. Filtering happens in memory because the field is a delimited text blob, not a relationship.

4.4 getSignerMappings(Id templateId)

Returns { Success: true, signers: [...] } ordered by Signer_Sequence__c ASC. Selected fields: Signer_Sequence__c, Template_Field_Name__c, Signer_Email_Field_API_Name__c, Signer_Name_Field_API_Name__c, Signer_Query__c.

4.5 upsertDocumentMapping(sFieldConfig, sObjectName, refrenceObjectName)

The most intricate method in the codebase. It takes the JSON of one Document_Data_Mapping__c, normalises the field path, derives the data type, and upserts.

Step 1 — the Today shortcut. If Salesforce_Field_API_Name__c equals Today (case-insensitive): set data type DATE, label Today's Date, Read_Only_On_Signing__c = true, upsert, return immediately.

Step 2 — dot-notation resolution (only when the API name contains . and Loop_Over_Child_Records__c is false). Splits on the first dot.

Child-mapping branch (Is_Child_Mapping__c = true):

  • Two segments (Child.Field) → resolve against refrenceObjectName.

  • Three segments (Child.Lookup.Field) → resolve the intermediate object via UtilityClass.getReferencedObject(), convert the lookup to relationship form (__c__r, OwnerIdOwner) and rebuild the path.

Parent-lookup branch: the left segment carries a composite key shaped ObjectName::LookupFieldApiName, produced by documentMappingField.handleParentFieldChange. The method splits on ::, converts the lookup field to relationship form, and rewrites the API name as Lookup__r.Field. Without :: the object cannot be resolved and sObjectName becomes null, so the data type falls back to TEXT.

Step 3 — data type derivation. Calls getFieldType() unless the incoming type is already IMAGE or CONDITIONAL — those are user-declared markers set in the config modal and must survive.

Step 4 — writeability guard. If isFieldWriteable() is false, force Read_Only_On_Signing__c = true and Required_On_Signing__c = false. This is the safety net preventing a signer being asked to edit a formula or rollup field.

Step 5 — defaults.

Condition

Default applied

Blank API name

Salesforce_Field_Data_Type__c = 'TEXT'

Loop_Over_Child_Records__c = true

TEXT

Blank Salesforce_Field_Label__c

Falls back to Document_Field_Name__c

Data type is Date

Date_Format__c = 'MM/DD/YYYY'

Returns { bSuccess, recordId } or { bSuccess: false, errorMessage }.

4.6 upsurtSigner (List<String> lstSigner)

Accepts a list of JSON strings, each a serialised Document_Signer_Mapping__c. Deserialises each, bulk-upserts, returns a StatusWrapper JSON. Called from two places with different intent:

  • documentSigners.js — one signer record (create or edit).

  • documentMappingField.js — one record per signer, rewriting Template_Field_Name__c so signature-field assignments persist.


Apex — PSPDFKitController

global with sharing. Backs the Visualforce page that hosts the PSPDFKit viewer inside the iframe. Its public properties are read by the VF markup; it is not a conventional LWC controller.

Member

Type

Purpose

getTemplateData()

VF getter

Primary entry point. Reads page params sFileId (a ContentVersion Id) and sTemplateId. Sets isPDFTemplate via isTemplatePDF() and isMappingScreen = true when a template Id is present. Base64-encodes the file into base64String, pulls the PSPDFKit licence from UtilityClass.getOrganizationKeys() key vfPageLicenseKey, and the org URL from UtilityClass.getCommonSetting('Organisation_URL').

getFileDetail()

VF getter

Alternative path keyed on page param id = ContentDocumentId; loads the latest version into conbase.

getbase64Data(String strId)

@AuraEnabled

Returns {ContentDocumentId, PathOnClient, VersionData} for direct LWC use.

isTemplatePDF(String sTemplateId)

Boolean

Reads Is_Document_PDF__c; defaults to true when the template is missing.

All page parameters pass through escapeHtml4() and all queries go through Data.read with bind maps.


Apex — DocumentTemplateTriggerHandler

Single method, performSystemValidations(List<Document_Template__c>), intended for a before insert / before update context (it uses addError).

Fetches Features_Entitlement__c once for the batch, then per record:

Record state

Action

Enable_eOriginal_Vaulting__c = true

Force Enable_Digital_Signature__c = true — vaulting implies digital signature

…and org lacks Eoriginal__c

addError — contact Cloud Maven Support

…and org lacks Digital_Signature__c

addError — contact Cloud Maven Support

Enable_Digital_Signature__c = true, vaulting off, org lacks Digital_Signature__c

addError

This is the server-side enforcement of the same rule the UI applies optimistically in documentTemplateDetail.handleChange.


The LWC ↔ Visualforce message contract

documentTemplateMapping registers window.addEventListener("message", …) in connectedCallback and builds the iframe URL as:

/apex/{pspdfKit_FramePageName}?context=Mapping&sFileId={contentVersionId}&sTemplateId={recordId}

Both pspdfKit_FramePageName and pspdfKit_VF_Origin come from custom labels exposed by the pSPDFKitLWC_Base class the component extends.

Message name

Direction

Payload

Handling

fieldDetails

VF → LWC

Array of form-field descriptors found in the document

prepareMappingOptions() merges them with existing mapping records; clears the loading state

selectedField

VF → LWC

Field name the user clicked in the viewer

Scrolls the matching documentMappingField into view (yOffset = -280), calls focus() on it and unfocus() on all others

saveFileToSalesForce

VF → LWC

Base64 of the edited PDF

Handled only in the dead documentSignerMapping component (§8.9)


LWC reference

8.1 createDocumentTemplate — creation wizard

Four-step wizard; currentStep is a string '1''4'.

Step

Screen

Getter

1

Template details (name, action, source object, sender, folder, email templates, description)

showDetailScreen

2

File upload

showFileUploadScreen

3

OTP configuration

showOTPScreen

4

Reminder configuration

showReminderScreen

cardHeader and helpText are switch getters keyed on the step number.

Wires: getObjectInfo on Document_Template__c; getPicklistValues for Template_Group_Folder__c and Template_Action__c; getOrgWideEmails; getAvailableObjects.

Function

Behaviour

connectedCallback()

isLoggedInUserAssignedLicense() → on completion, getTemplateGrouping() only if licensed

acceptedFormats (getter)

.pdf .doc .docx .xlsx .xlsm .csv .pptx .ppt .xls

handleChange(event)

Routes by data-id. enableReminder is component state; OTP and eSign checkboxes read .checked; everything else reads .value. Changing the source object triggers getEmailTemplates().

handleUploadFinished(event)

Captures documentId, calls isSizeWithtinAllowedLimit; on failure shows a warning toast and clears documentId, blocking creation. Sets Is_Document_PDF__c from mimeType === 'application/pdf'.

handleClick()

Steps 1–3: validateData() and a non-empty source object → advance. Step 4: strip OTP fields when OTP_Required__c is false, strip reminder fields when enableReminder is false, set Flatten_document_while_generation__c = !Send_Document_For_eSignature__c, call createTemplate, navigate to the new record.

validateData()

reportValidity() + checkValidity() across all lightning-input and lightning-combobox elements

getEmailTemplates(obj)

Apex call; sets bEmailTeamplatesAvailable = false when templates exist (inverted flag — the markup disables the picker when true)

allowOnlyDigits / allowOnlyDigitsPaste

Keydown and paste filters for the numeric OTP/reminder inputs

The flatten rule (repeated in documentTemplateDetail): a document going out for signature must keep its form fields interactive, so flattening is disabled. A generate-only document is flattened.

8.2 documentTemplateDetail — record-page editor

Accordion sections: templateDetails, otpDetails, advancedConfigurations, reminderDetails.

Member

Behaviour

@api templateDetails, emailTemplates, orgWideEmails

Supplied by the parent record page component

@wire enabledServices

Sets digitalSignEnabled; eOriginalEnabled = data.eOriginal && digitalSign — vaulting is never offered without digital signature

showAdvancedSection (getter)

eOriginalEnabled || digitalSignEnabled — hides the whole Advanced block for unentitled orgs

isTemplateCreateRecord (getter)

Template_Action__c === 'Create Record'

renderedCallback()

Reads Status__c and paints the toggle button red + "Deactivate Template", or green + "Activate Template"

handleChange(event)

Checkbox data-ids are matched against an explicit list; ticking Enable_eOriginal_Vaulting__c also sets Enable_Digital_Signature__c = true (mirrors the trigger rule in §6)

handleClick(event)

editTemplate → apply the flatten rule, send the whole record. editStatus → send only {Status__c, Id} with the value flipped.

editTemplate(oDocTemplate)

Calls Apex, parses the JSON wrapper, fires an edit custom event with recordId + recordDetails, toasts, then window.location.reload()

handleError(error)

Normalises array-body vs body.message errors into a toast

8.3 cloneDocumentTemplate — quick action

Record quick action; extends NavigationMixin.

Member

Behaviour

set recordId(value)

Setter fires fetchTemplateName() as soon as the framework injects the Id

fetchTemplateName(id)

getTemplateName → prefills cloningConfigurations.templateName as "{original} (Cloned)"; always clears isLoading in finally

cloningConfigurations

{ templateName, cloneDataFieldMappings: true, cloneSignersMappings: true, copyDocument: true, autoActivate: false }

handleToggle* handlers

One per toggle; each writes a key on cloningConfigurations

handleConfirm()

Guards on missing record Id and empty name → cloneTemplate → close action, toast, navigate to the new record

8.4 documentTemplateMapping — mapping workspace (parent)

Extends pSPDFKitLWC_Base. Layout is 8/12 iframe + 4/12 tabset (Mapping, Signers).

Function

Behaviour

connectedCallback()

Registers the pre-bound message listener, then Promise.all([getScreenConfigs(), getSignerMappings()])

getScreenConfigs()

Destructures the Apex response. Sets primarySObject, bDocumentPDF, childConfigs, existingMappings. For Word templates it remaps childConfigs to {label, value: apiName} and derives loopFields from existing mappings where Loop_Over_Child_Records__c is true, tracking their Ids in loopFieldIds. Sets templateUploaded = !!contentVersionId, builds the iframe url, and builds dropdownOptions (adding a synthetic Today's DateTODAY entry) plus lookupFieldsArr (all REFERENCE fields), sorted case-insensitively by label.

handleVfMessages(message)

See §7

prepareMappingOptions(fields)

For each form field from the document, finds the matching Document_Data_Mapping__c by Document_Field_Name__c. Sets a display value string ("Account > Billing City", or the signing-date sentence when Show_Current_Date__c), copies all mapping fields onto the field object, and sets exactly one type flag from Salesforce_Field_Data_Type__c: isDateField, isEmailField, isNumberField, isPercent, isBooleanField (from CONDITIONAL), isImageField, isCurrency, isPicklistField.

getSignerMappings()

Builds three structures from the signer records: availableSigners ({label: "Signer N", value: Id}), signerFieldMapping (signerId → "FieldA;FieldB"), and fieldSignerMapping (fieldName → signerId, first writer wins). The reverse map is how each field row knows who signs it.

handleLoopFieldsUpdate(e)

Adds a newly saved loop field to loopFields if its Id is not already in loopFieldIds, then pushes the array into every child via updateLoopArr()

handleLoopFieldsDelete(e)

Removes by matching both label and value, deletes the Id from the set, re-pushes to children

handleSignerMapping(e)

Replaces fieldSignerMapping with the Map a child emitted

8.5 documentMappingField — one field row + config modal

The busiest component. One instance per form field found in the document.

Public API

Member

Purpose

@api oField

The field descriptor — copied to localField on connect so edits stay local until saved

@api childConfigs, loopFields, signers, lookupArr, fieldSignerMapping, primaryObj, templateId, pdfDoc

Context from the parent

@api set optionValues(value)

Freezes the dropdown list to avoid reactivity churn across hundreds of rows

@api focus() / unfocus()

Called by the parent when the user clicks a field in the PDF viewer

@api updateLoopArr(value)

Refreshes metaDataLabels when the loop-field list changes

Getters

Getter

Returns

bFieldMapped

Signature fields: is this label in fieldSignerMapping? Others: does localField.Id exist?

isSignature

localField.isSignatureField — set by the viewer, not by Apex

labelValue

localField.label — the document field name, used as the map key everywhere

badgeConfig

Builds the status badge. Sequential overwrite, not accumulation — later matches replace earlier ones, so priority runs read-only → required-on-signing → required-to-generate → loop → signature → image → conditional → signing date, and only the last match renders.

showMappingBox

Whether to render the "mapped to" summary

signerLabel

"This signature box will be signed by Signer N"

Selection handlers

Handler

Behaviour

handlesObjectFieldChamnge(event) (sic)

If the chosen field is in lookupArr, fetch its related fields and reveal the second dropdown (className → half width). Otherwise write the API name directly and build the label as "Primary > Field Label".

handleParentFieldChange(event)

Builds the composite path Object__r::LookupId.Field that §4.5 parses server-side

handleMetaDataChange(event)

Child-object selection. PDF: resolve the child sObject from the custom metadata row. Word: the selected value is the object name. Then load that object's fields.

handleChildObjFieldChange / handleChildLookupChange

Assemble two- and three-segment child paths

handleChange(event)

Switch on data-id: the three boolean signing flags and Show_Current_Date__c; Is_Child_Mapping__c; isImageField / isConditionField (write the sentinel data types IMAGE / CONDITIONAL); Loop_Over_Child_Records__c; label (loop label); signerMapping (writes fieldSignerMapping.set(fieldLabel, signerId))

Save and delete

handleMappingSaveAction():

  1. Signature field → upsurtSigner(createSignerConfig()) and stop.

  2. validateForm() over .validSec1 elements.

  3. Reject read-only and required-on-signing together.

  4. Loop branch: set Loop_Over_Child_Records__c, build the API name as child.field, use the user's loop label, force type TEXT, reject a duplicate loop label for the same sObject, and pull the WHERE clause from the nested c-dynamic-query-creation-l-w-c when enabled.

  5. Stamp Document_Template__c and Document_Field_Name__c, call upsertDocumentMapping, and on success emit update if this is a loop field.

deleteMapping() — signature fields delete from fieldSignerMapping and re-save all signers; other fields call deleteRecord(), reset every local variable, and emit loopdelete when applicable.

createSignerConfig() — inverts fieldSignerMapping into per-signer groups and returns an array of JSON strings shaped {Id, Template_Field_Name__c: "A;B;C"} — exactly what upsurtSigner expects.

parseFieldReference() — the reverse of the save logic, run when opening an existing mapping for edit. Splits the stored API name back into loop / lookup / child-lookup form, converts __r back to __c (and bare relationship names back to …Id), and reloads the dependent dropdowns.

8.6 documentSigners — signer list and SOQL builder

Renders the signer cards and hosts the add/edit modal containing a visual SOQL builder.

Wires

Wire

Purpose

getAvailableObjects

Object picker

getsObjectFields({ sObjectName: '$objectApiName' })

Reactive — refires whenever the selected object changes. Contains the branching that rebuilds or repopulates WHERE state (add mode vs edit mode vs object-changed-in-edit-mode).

getRecord on Primary_sObject__c

Establishes initialObjectApiName, the template's source object

Query construction

generatedQuery (getter) assembles:

SQL
SELECT Id, {emailField}, {nameField} FROM {objectApiName}
  [WHERE cond1 AND cond2 …]
  [ORDER BY {field} {ASC|DESC}]
  [LIMIT n [OFFSET m]]

Value formatting is type-aware: numerics unquoted; booleans normalised to true/false; dates quoted unless they start with TODAY; everything else single-quoted with embedded quotes escaped. The literal ParentId is emitted unquoted — it is a runtime placeholder the generation engine substitutes with the actual record Id. If the admin hand-edits the query, queryEdited flips and the getter returns their text verbatim.

expressionOptionsByType maps each Salesforce data type to its legal operators: equality only for text/picklist/reference/id/email/phone/url; comparisons for numerics and dates; INCLUDES/EXCLUDES for multipicklist.

Edit-mode round trip — the subtlest flow in the component:

  1. handleEdit captures the signer, regex-extracts the object from FROM\s+(\w+), and stores the record in _pendingSignerEdit.

  2. If the object differs from the current one, setting objectApiName refires the getsObjectFields wire.

  3. When the wire returns, repopulateFieldsAfterWire() runs — it needs sObjectFields present to normalise field API-name casing.

  4. Name and email fields are resolved: dot-notation paths are split, the relationship name is reversed to a lookup API name (AccountAccountId, Obj__rObj__c), the parent object is found via referenceTo, its fields are fetched, and the dot-notation is rebuilt canonically.

  5. parseQueryIntoState() regex-extracts ORDER BY, LIMIT, OFFSET and each WHERE condition back into whereConditions. The first row is locked (disableName when the field is Id, disableOperator always) to protect the parent-record binding.

Other function

Behaviour

addSigner()

resetState(), auto-assign Signer_Sequence__c = max(existing) + 1, seed the mandatory first WHERE row (Id = ParentId)

handleNameChange / handleEmailChange

If the chosen field is a reference, fetch the parent object's fields (filtering out further references — one level only) and show the secondary dropdown

buildRelationshipName / reverseBuildRelationshipName

AccountId ↔ Account, Obj__c ↔ Obj__r

normalizeFieldApiName / normalizeFieldFromList

Case-correct a field API name against a known field list

handleSave()

Validates required fields, lookup sub-selections, and a fully-populated first WHERE row; then upsurtSigner

getSignerMappings()

Reloads and labels signers Signer 1…N, then refreshSignerComponents() on the next tick

validateSigner()

Returns true unconditionally — a stub

8.7 documentSignerIndividual — one signer card

Member

Behaviour

@api signerId, label, signerIndex

Set by the parent

@api refreshRecord()

notifyRecordUpdateAvailable — how the parent forces the read-only form to re-read after a save

renderedCallback()

Runs once (guarded by bRenderedCallbackRan); applies a coloured left border from a fixed palette indexed by signerIndex, falling back to random when the index exceeds the palette

Record display

lightning-record-view-form showing Signer_Email_Field_API_Name__c, Signer_Name_Field_API_Name__c, Signer_Sequence__c, Signer_Query__c

handleEdit()

Emits edit with {signerId}

handleDelete() / deleteSigner()

Confirmation modal → deleteRecord() → emits delete with {label}

handleMenuChange(event)

Sets currentOperation to readOnly or mask from data-value, then opens the configuration modal

8.8 documentSignerConfiguration — read-only / mask field picker

A dual-listbox modal, reused for two purposes via the method property. Backed by DocumentSigningUtility, not by the controllers above.

Member

Behaviour

@api recordId (the signer), method (readOnly | mask), label

Inputs

methodLabel (getter)

Returns the explanatory sentence for the active mode

getSignerConfiguration()

Calls DocumentSigningUtility.getSignerConfiguration, splits Masked_Fields__c and Read_Only_Fields__c on ;, and populates values with whichever list matches method. In mask mode it also loads the read-only list into readOnlyFields so it can be preserved on save. Builds availableOptions as {value: Document_Field_Name__c, label: Salesforce_Field_Label__c}.

handleClick()

Mask mode: every selected field is added to both the masked list and the read-only set — masking implies read-only. Read-only mode: the selection becomes the read-only set and masked is sent as null. Then saveSignerConfiguration.

saveSignerConfiguration(readOnly, masked)

Calls DocumentSigningUtility.saveSignerConfiguration with semicolon-joined strings

Warning: in read-only mode masked is set to null, which wipes any existing masked-field configuration. This interaction needs a product decision before release.

Cross-cutting conventions

Convention

Detail

Delimited storage

Template_Field_Name__c, Masked_Fields__c, Read_Only_Fields__c and Child_Object_Mapping_Configurations__mdt.Document_Template__c all store semicolon-delimited lists. Filtering on these happens in Apex or JS, never in SOQL.

PDF vs Word

Is_Document_PDF__c forks behaviour in getScreenConfigurations (metadata vs real child relationships), in documentTemplateMapping (loop-field derivation), and in documentMappingField (child dropdown source).

Field-path encoding

Obj__r::LookupId.Field for parent lookups; Child.Field and Child.Lookup.Field for child mappings; Today / TODAY as date sentinels; ParentId as the runtime record placeholder in signer queries.

Error surfacing

Every LWC has a near-identical handleError flattening error.body (array or .message) into a toast. Candidate for extraction into a shared module.