public with sharing class CLMAdminService { public class CaseDeficiencyItem { @AuraEnabled public Id recordId; @AuraEnabled public Decimal deficiencyNumber; @AuraEnabled public String description; @AuraEnabled public String resolution; @AuraEnabled public String reference; } public class CaseContext { @AuraEnabled public Id caseId; @AuraEnabled public String caseNumber; @AuraEnabled public String propertyAddress; @AuraEnabled public String lastDocGenStatus; @AuraEnabled public String lastDocGenMessage; @AuraEnabled public String lastClmAccountCode; @AuraEnabled public String lastDocGenTaskId; @AuraEnabled public String lastDocGenTaskUrl; @AuraEnabled public String generatedDocumentUrl; @AuraEnabled public String generatedDocumentId; @AuraEnabled public String attachedFileContentDocumentId; @AuraEnabled public String attachedFileUrl; @AuraEnabled public Datetime lastDocGenRequestedAt; @AuraEnabled public Datetime lastDocGenCompletedAt; @AuraEnabled public String eSignatureEnvelopeId; @AuraEnabled public String eSignatureEnvelopeStatus; @AuraEnabled public String eSignatureEnvelopeUrl; @AuraEnabled public Datetime eSignatureSentAt; @AuraEnabled public Datetime eSignatureCompletedAt; @AuraEnabled public List deficiencies; } public class FileAttachmentResult { @AuraEnabled public Boolean success; @AuraEnabled public String message; @AuraEnabled public String contentDocumentId; @AuraEnabled public String fileUrl; @AuraEnabled public String fileTitle; } public class AccountSettings { @AuraEnabled public String accountCode; @AuraEnabled public String accountDisplayName; @AuraEnabled public String environment; @AuraEnabled public String clmAccountId; @AuraEnabled public String clmApiNamedCredential; @AuraEnabled public String clmDownloadNamedCredential; @AuraEnabled public String eSignatureRestNamedCredential; @AuraEnabled public String templateRootFolderHref; @AuraEnabled public String destinationRootFolderHref; @AuraEnabled public String defaultTemplateDocumentHref; @AuraEnabled public String defaultDocumentNamePrefix; @AuraEnabled public Boolean active; } public class LetterSettings { @AuraEnabled public String accountCode; @AuraEnabled public String letterCode; @AuraEnabled public String letterDisplayName; @AuraEnabled public String description; @AuraEnabled public Boolean isDefault; @AuraEnabled public Decimal sortOrder; @AuraEnabled public String templateRootFolderHref; @AuraEnabled public String destinationRootFolderHref; @AuraEnabled public String defaultTemplateDocumentHref; @AuraEnabled public String defaultDocumentNamePrefix; @AuraEnabled public Boolean active; } public class ResourceItem { @AuraEnabled public String name; @AuraEnabled public String href; @AuraEnabled public String type; @AuraEnabled public String parentHref; @AuraEnabled public String rawJson; } public class FolderContents { @AuraEnabled public ResourceItem folder; @AuraEnabled public List folders; @AuraEnabled public List documents; } public class DocGenPreview { @AuraEnabled public String accountCode; @AuraEnabled public String accountDisplayName; @AuraEnabled public String letterCode; @AuraEnabled public String letterDisplayName; @AuraEnabled public String mergeTaskEndpointUrl; @AuraEnabled public String templateDocHref; @AuraEnabled public String destinationFolderHref; @AuraEnabled public String destinationDocName; @AuraEnabled public String payloadJson; @AuraEnabled public String dataXml; @AuraEnabled public String requestBodyJson; } @AuraEnabled(cacheable=true) public static List listAccountSettings() { List settings = new List(); for (CLM_Account_Setting__mdt row : [ SELECT DeveloperName, Account_Code__c, Account_Display_Name__c, Environment_Code__c, CLM_Account_Id__c, CLM_Api_Named_Credential__c, CLM_Download_Named_Credential__c, ESignature_Rest_Named_Credential__c, Template_Root_Folder_Href__c, Destination_Root_Folder_Href__c, Default_Template_Document_Href__c, Default_Destination_Document_Name_Prefix__c, Active__c FROM CLM_Account_Setting__mdt WHERE Active__c = true ORDER BY Account_Display_Name__c ASC, DeveloperName ASC ]) { settings.add(toAccountSettings(row)); } return settings; } @AuraEnabled(cacheable=true) public static AccountSettings getAccountSettings(String accountCode) { CLM_Account_Setting__mdt row = resolveAccountSetting(accountCode); return row == null ? null : toAccountSettings(row); } @AuraEnabled(cacheable=true) public static List listLetterSettings(String accountCode) { AccountSettings account = getAccountSettings(accountCode); List letters = new List(); if (account == null) { return letters; } for (CLM_Letter_Definition__mdt row : [ SELECT DeveloperName, Account_Code__c, Letter_Code__c, Letter_Display_Name__c, Description__c, Active__c, Is_Default__c, Sort_Order__c, Template_Root_Folder_Href__c, Destination_Root_Folder_Href__c, Default_Template_Document_Href__c, Default_Destination_Document_Name_Prefix__c FROM CLM_Letter_Definition__mdt WHERE Active__c = true AND Account_Code__c = :account.accountCode ORDER BY Is_Default__c DESC, Sort_Order__c ASC, Letter_Display_Name__c ASC, DeveloperName ASC ]) { letters.add(toLetterSettings(row, account)); } if (letters.isEmpty()) { letters.add(buildFallbackLetterSettings(account)); } return letters; } @AuraEnabled(cacheable=true) public static LetterSettings getLetterSettings(String accountCode, String letterCode) { AccountSettings account = getAccountSettings(accountCode); if (account == null) { return null; } String normalizedLetterCode = String.isBlank(letterCode) ? null : letterCode.trim(); if (String.isNotBlank(normalizedLetterCode)) { List rows = [ SELECT DeveloperName, Account_Code__c, Letter_Code__c, Letter_Display_Name__c, Description__c, Active__c, Is_Default__c, Sort_Order__c, Template_Root_Folder_Href__c, Destination_Root_Folder_Href__c, Default_Template_Document_Href__c, Default_Destination_Document_Name_Prefix__c FROM CLM_Letter_Definition__mdt WHERE Active__c = true AND Account_Code__c = :account.accountCode AND Letter_Code__c = :normalizedLetterCode LIMIT 1 ]; if (!rows.isEmpty()) { return toLetterSettings(rows[0], account); } } List letters = listLetterSettings(account.accountCode); for (LetterSettings letter : letters) { if (letter.isDefault) { return letter; } } return letters.isEmpty() ? buildFallbackLetterSettings(account) : letters[0]; } @AuraEnabled(cacheable=false) public static CLMDocGenCallout.CLMDocGenResponse generateDocument( Id appraiserCaseId, String templateDocHref, String destinationFolderHref, String destinationDocName, String accountCode ) { CLM_Account_Setting__mdt account = requireAccountSetting(accountCode); CLMDocGenCallout.CLMDocGenResponse response = CLMDocGenCallout.generateDocument( (String) appraiserCaseId, templateDocHref, destinationFolderHref, destinationDocName, account.Environment_Code__c, account.CLM_Account_Id__c, account.CLM_Api_Named_Credential__c ); persistDocGenResult(appraiserCaseId, templateDocHref, destinationFolderHref, response, false, accountCode); return response; } @AuraEnabled(cacheable=false) public static CLMDocGenCallout.CLMDocGenResponse getTaskStatus(Id appraiserCaseId, String taskId, String accountCode) { CLM_Account_Setting__mdt account = requireAccountSetting(accountCode); CLMDocGenCallout.CLMDocGenResponse response = CLMDocGenCallout.getTaskStatus( taskId, account.Environment_Code__c, account.CLM_Account_Id__c, account.CLM_Api_Named_Credential__c ); persistDocGenResult(appraiserCaseId, null, null, response, true, accountCode); return response; } @AuraEnabled(cacheable=false) public static String probeResource(String resourceOrHref, String accountCode) { CLM_Account_Setting__mdt account = requireAccountSetting(accountCode); return performGet(resourceOrHref, account).getBody(); } @AuraEnabled(cacheable=false) public static DocGenPreview getDocGenPreview(Id appraiserCaseId, String accountCode, String letterCode) { if (appraiserCaseId == null) { throw new AuraHandledException('appraiserCaseId is required'); } AccountSettings account = getAccountSettings(accountCode); if (account == null) { throw new AuraHandledException('No active CLM account setting was found for ' + accountCode + '.'); } LetterSettings letter = getLetterSettings(account.accountCode, letterCode); List previewRows = [ SELECT Id, Name FROM Appraiser_Case__c WHERE Id = :appraiserCaseId LIMIT 1 ]; if (previewRows.isEmpty()) { throw new AuraHandledException('Appraiser Case not found: ' + appraiserCaseId); } Appraiser_Case__c appraiserCase = previewRows[0]; String prefix = letter != null && String.isNotBlank(letter.defaultDocumentNamePrefix) ? letter.defaultDocumentNamePrefix : account.defaultDocumentNamePrefix; DocGenPreview preview = new DocGenPreview(); preview.accountCode = account.accountCode; preview.accountDisplayName = account.accountDisplayName; preview.letterCode = letter != null ? letter.letterCode : 'APPRAISER_REVIEW'; preview.letterDisplayName = letter != null ? letter.letterDisplayName : 'Appraiser Review Letter'; preview.templateDocHref = letter != null ? letter.defaultTemplateDocumentHref : account.defaultTemplateDocumentHref; preview.destinationFolderHref = letter != null ? letter.destinationRootFolderHref : account.destinationRootFolderHref; preview.destinationDocName = buildDefaultDocumentName(prefix, appraiserCase.Name); preview.mergeTaskEndpointUrl = CLMDocGenCallout.buildDocumentXmlMergeTasksUrl( preview.templateDocHref, preview.destinationFolderHref, account.environment, account.clmAccountId ); preview.payloadJson = JSON.serializePretty(AppraiserCasePayloadBuilder.buildPayload((String) appraiserCaseId)); preview.dataXml = CLMDocGenCallout.prettyPrintXml(CLMDocGenCallout.buildDataXmlForCase((String) appraiserCaseId)); preview.requestBodyJson = CLMDocGenCallout.buildRequestBodyJson( (String) appraiserCaseId, preview.templateDocHref, preview.destinationFolderHref, preview.destinationDocName ); return preview; } @AuraEnabled(cacheable=false) public static CaseContext getCaseContext(Id appraiserCaseId) { List contextRows = [ SELECT Id, Name, Property_Street__c, Property_City__c, Property_State_Province__c, Property_Postal_Code__c, Property_Country__c, Last_DocGen_Status__c, Last_DocGen_Message__c, Last_CLM_Account_Code__c, Last_DocGen_Task_Id__c, Last_DocGen_Task_Url__c, Generated_Document_Url__c, Generated_Document_Id__c, Attached_File_Content_Document_Id__c, Attached_File_Url__c, Last_DocGen_Requested_At__c, Last_DocGen_Completed_At__c, ESignature_Envelope_Id__c, ESignature_Envelope_Status__c, ESignature_Sent_At__c, ESignature_Completed_At__c, ESignature_Envelope_Url__c, (SELECT Id, Deficiency_Number__c, Description__c, Resolution__c, Reference__c FROM Deficiencies__r ORDER BY Deficiency_Number__c ASC, CreatedDate ASC) FROM Appraiser_Case__c WHERE Id = :appraiserCaseId LIMIT 1 ]; if (contextRows.isEmpty()) { throw new AuraHandledException('Appraiser Case not found: ' + appraiserCaseId); } Appraiser_Case__c appraiserCase = contextRows[0]; CaseContext context = new CaseContext(); context.caseId = appraiserCase.Id; context.caseNumber = appraiserCase.Name; context.propertyAddress = formatAddress(appraiserCase); context.lastDocGenStatus = appraiserCase.Last_DocGen_Status__c; context.lastDocGenMessage = appraiserCase.Last_DocGen_Message__c; context.lastClmAccountCode = appraiserCase.Last_CLM_Account_Code__c; context.lastDocGenTaskId = appraiserCase.Last_DocGen_Task_Id__c; context.lastDocGenTaskUrl = appraiserCase.Last_DocGen_Task_Url__c; context.generatedDocumentUrl = appraiserCase.Generated_Document_Url__c; context.generatedDocumentId = appraiserCase.Generated_Document_Id__c; context.attachedFileContentDocumentId = appraiserCase.Attached_File_Content_Document_Id__c; context.attachedFileUrl = appraiserCase.Attached_File_Url__c; context.lastDocGenRequestedAt = appraiserCase.Last_DocGen_Requested_At__c; context.lastDocGenCompletedAt = appraiserCase.Last_DocGen_Completed_At__c; context.eSignatureEnvelopeId = appraiserCase.ESignature_Envelope_Id__c; context.eSignatureEnvelopeStatus = appraiserCase.ESignature_Envelope_Status__c; context.eSignatureEnvelopeUrl = appraiserCase.ESignature_Envelope_Url__c; context.eSignatureSentAt = appraiserCase.ESignature_Sent_At__c; context.eSignatureCompletedAt = appraiserCase.ESignature_Completed_At__c; context.deficiencies = new List(); if (appraiserCase.Deficiencies__r != null) { for (Appraiser_Case_Deficiency__c deficiency : appraiserCase.Deficiencies__r) { CaseDeficiencyItem item = new CaseDeficiencyItem(); item.recordId = deficiency.Id; item.deficiencyNumber = deficiency.Deficiency_Number__c; item.description = deficiency.Description__c; item.resolution = deficiency.Resolution__c; item.reference = deficiency.Reference__c; context.deficiencies.add(item); } } return context; } @AuraEnabled(cacheable=false) public static FileAttachmentResult attachGeneratedDocumentToCase(Id appraiserCaseId, String accountCode) { if (appraiserCaseId == null) { throw new AuraHandledException('appraiserCaseId is required'); } CLM_Account_Setting__mdt account = requireAccountSetting(accountCode); Appraiser_Case__c appraiserCase = [ SELECT Id, Name, Generated_Document_Url__c, Generated_Document_Id__c FROM Appraiser_Case__c WHERE Id = :appraiserCaseId LIMIT 1 ]; if (String.isBlank(appraiserCase.Generated_Document_Url__c)) { throw new AuraHandledException('No generated document is available to attach yet.'); } CLMDocGenCallout.DownloadedDocument downloaded = CLMDocGenCallout.downloadDocument( appraiserCase.Generated_Document_Url__c, account.Environment_Code__c, account.CLM_Account_Id__c, account.CLM_Download_Named_Credential__c ); String fileName = String.isNotBlank(downloaded.fileName) ? downloaded.fileName : 'Generated_' + appraiserCase.Name + '.docx'; String title = fileName.contains('.') ? fileName.substringBeforeLast('.') : fileName; ContentVersion version = new ContentVersion( Title = title, PathOnClient = '/' + fileName, VersionData = downloaded.body ); insert version; version = [ SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id = :version.Id LIMIT 1 ]; insert new ContentDocumentLink( ContentDocumentId = version.ContentDocumentId, LinkedEntityId = appraiserCase.Id, ShareType = 'V', Visibility = 'AllUsers' ); String fileUrl = '/lightning/r/ContentDocument/' + version.ContentDocumentId + '/view'; update new Appraiser_Case__c( Id = appraiserCase.Id, Last_CLM_Account_Code__c = accountCode, Attached_File_Content_Document_Id__c = version.ContentDocumentId, Attached_File_Url__c = fileUrl ); FileAttachmentResult result = new FileAttachmentResult(); result.success = true; result.message = 'Generated document attached to the case.'; result.contentDocumentId = version.ContentDocumentId; result.fileUrl = fileUrl; result.fileTitle = title; return result; } @AuraEnabled(cacheable=false) public static void persistEnvelopeResult( Id caseId, String envelopeId, String envelopeStatus, String envelopeUri ) { if (caseId == null) { return; } Appraiser_Case__c updateCase = new Appraiser_Case__c(Id = caseId); updateCase.ESignature_Envelope_Id__c = envelopeId; updateCase.ESignature_Envelope_Status__c = envelopeStatus; updateCase.ESignature_Sent_At__c = System.now(); if (String.isNotBlank(envelopeUri)) { updateCase.ESignature_Envelope_Url__c = envelopeUri; } update updateCase; } private static String formatAddress(Appraiser_Case__c appraiserCase) { return AppraiserCasePayloadBuilder.formatMailingAddress( appraiserCase.Property_Street__c, appraiserCase.Property_City__c, appraiserCase.Property_State_Province__c, appraiserCase.Property_Postal_Code__c, appraiserCase.Property_Country__c ); } @AuraEnabled(cacheable=false) public static FolderContents getFolderContents(String folderHref, String accountCode) { if (String.isBlank(folderHref)) { throw new IllegalArgumentException('folderHref is required'); } CLM_Account_Setting__mdt account = requireAccountSetting(accountCode); FolderContents contents = new FolderContents(); contents.folder = parseSingleResource(performGet(folderHref, account).getBody(), 'Folder'); contents.folders = parseResourceList(performGet(folderHref + '/folders', account).getBody(), 'Folder', folderHref); contents.documents = parseResourceList(performGet(folderHref + '/documents', account).getBody(), 'Document', folderHref); return contents; } private static HttpResponse performGet(String resourceOrHref, CLM_Account_Setting__mdt account) { HttpRequest req = new HttpRequest(); req.setEndpoint(CLMDocGenCallout.buildEndpointForResource( resourceOrHref, account.CLM_Account_Id__c, account.CLM_Api_Named_Credential__c )); req.setMethod('GET'); req.setTimeout(CLMDocGenCallout.HTTP_TIMEOUT); HttpResponse res = new Http().send(req); Integer statusCode = res.getStatusCode(); if (statusCode < 200 || statusCode >= 300) { throw new AuraHandledException('CLM API Error (HTTP ' + statusCode + '): ' + res.getBody()); } return res; } @TestVisible private static ResourceItem parseSingleResource(String body, String defaultType) { Object root = JSON.deserializeUntyped(body); if (!(root instanceof Map)) { return null; } return parseResource((Map) root, defaultType, null); } @TestVisible private static List parseResourceList(String body, String defaultType, String parentHref) { Object root = JSON.deserializeUntyped(body); List records = unwrapList(root); List items = new List(); for (Object record : records) { if (record instanceof Map) { items.add(parseResource((Map) record, defaultType, parentHref)); } } return items; } private static List unwrapList(Object root) { if (root instanceof List) { return (List) root; } if (root instanceof Map) { Map payload = (Map) root; for (String key : new List{ 'Results', 'Items', 'Documents', 'Folders' }) { Object value = payload.get(key); if (value instanceof List) { return (List) value; } } if (payload.size() == 1) { for (Object value : payload.values()) { if (value instanceof List) { return (List) value; } } } } return new List(); } private static ResourceItem parseResource(Map source, String defaultType, String parentHref) { ResourceItem item = new ResourceItem(); item.name = firstString(source, new List{ 'Name', 'DisplayName', 'Title', 'Label' }); item.href = firstString(source, new List{ 'Href', 'Uri', 'Location' }); item.type = firstString(source, new List{ 'Type', 'ObjectType', 'ItemType' }); item.parentHref = extractParentHref(source, parentHref); item.rawJson = JSON.serialize(source); if (String.isBlank(item.type)) { item.type = defaultType; } if (String.isBlank(item.name) && String.isNotBlank(item.href)) { item.name = item.href.substringAfterLast('/'); } return item; } private static String extractParentHref(Map source, String fallbackValue) { Object parentValue = source.get('Parent'); if (parentValue instanceof Map) { String href = firstString((Map) parentValue, new List{ 'Href', 'Uri', 'Location' }); if (String.isNotBlank(href)) { return href; } } return fallbackValue; } private static String firstString(Map source, List keys) { for (String key : keys) { Object value = source.get(key); if (value != null) { String textValue = String.valueOf(value); if (String.isNotBlank(textValue)) { return textValue; } } } return null; } private static void persistDocGenResult( Id appraiserCaseId, String templateDocHref, String destinationFolderHref, CLMDocGenCallout.CLMDocGenResponse response, Boolean isStatusRefresh, String accountCode ) { if (appraiserCaseId == null || response == null) { return; } Appraiser_Case__c updateCase = new Appraiser_Case__c(Id = appraiserCaseId); updateCase.Last_CLM_Account_Code__c = accountCode; updateCase.Last_DocGen_Status__c = String.isNotBlank(response.taskStatus) ? response.taskStatus : (response.success ? 'Submitted' : 'Failed'); updateCase.Last_DocGen_Message__c = response.message; updateCase.Last_DocGen_Task_Id__c = response.documentId; updateCase.Last_DocGen_Task_Url__c = response.documentUrl; if (!isStatusRefresh) { updateCase.Last_DocGen_Requested_At__c = System.now(); updateCase.Last_Template_Document_Href__c = templateDocHref; updateCase.Last_Destination_Folder_Href__c = destinationFolderHref; } if (String.isNotBlank(response.generatedDocumentUrl)) { updateCase.Generated_Document_Url__c = response.generatedDocumentUrl; updateCase.Generated_Document_Id__c = response.generatedDocumentId; } if (response.success && String.valueOf(response.taskStatus).toLowerCase() == 'completed') { updateCase.Last_DocGen_Completed_At__c = System.now(); } update updateCase; } private static CLM_Account_Setting__mdt requireAccountSetting(String accountCode) { CLM_Account_Setting__mdt row = resolveAccountSetting(accountCode); if (row == null) { throw new AuraHandledException('No active CLM account setting was found for ' + accountCode + '.'); } return row; } private static CLM_Account_Setting__mdt resolveAccountSetting(String accountCode) { String normalizedCode = String.isBlank(accountCode) ? null : accountCode.trim(); List rows; if (String.isNotBlank(normalizedCode)) { rows = [ SELECT DeveloperName, Account_Code__c, Account_Display_Name__c, Environment_Code__c, CLM_Account_Id__c, CLM_Api_Named_Credential__c, CLM_Download_Named_Credential__c, ESignature_Rest_Named_Credential__c, Template_Root_Folder_Href__c, Destination_Root_Folder_Href__c, Default_Template_Document_Href__c, Default_Destination_Document_Name_Prefix__c, Active__c FROM CLM_Account_Setting__mdt WHERE Active__c = true AND DeveloperName = :normalizedCode LIMIT 1 ]; if (rows.isEmpty()) { rows = [ SELECT DeveloperName, Account_Code__c, Account_Display_Name__c, Environment_Code__c, CLM_Account_Id__c, CLM_Api_Named_Credential__c, CLM_Download_Named_Credential__c, ESignature_Rest_Named_Credential__c, Template_Root_Folder_Href__c, Destination_Root_Folder_Href__c, Default_Template_Document_Href__c, Default_Destination_Document_Name_Prefix__c, Active__c FROM CLM_Account_Setting__mdt WHERE Active__c = true AND Account_Code__c = :normalizedCode LIMIT 1 ]; } } else { rows = [ SELECT DeveloperName, Account_Code__c, Account_Display_Name__c, Environment_Code__c, CLM_Account_Id__c, CLM_Api_Named_Credential__c, CLM_Download_Named_Credential__c, ESignature_Rest_Named_Credential__c, Template_Root_Folder_Href__c, Destination_Root_Folder_Href__c, Default_Template_Document_Href__c, Default_Destination_Document_Name_Prefix__c, Active__c FROM CLM_Account_Setting__mdt WHERE Active__c = true ORDER BY Account_Display_Name__c ASC, DeveloperName ASC LIMIT 1 ]; } return rows.isEmpty() ? null : rows[0]; } private static AccountSettings toAccountSettings(CLM_Account_Setting__mdt row) { AccountSettings settings = new AccountSettings(); settings.accountCode = String.isNotBlank(row.Account_Code__c) ? row.Account_Code__c : row.DeveloperName; settings.accountDisplayName = String.isNotBlank(row.Account_Display_Name__c) ? row.Account_Display_Name__c : row.DeveloperName; settings.environment = row.Environment_Code__c; settings.clmAccountId = row.CLM_Account_Id__c; settings.clmApiNamedCredential = row.CLM_Api_Named_Credential__c; settings.clmDownloadNamedCredential = row.CLM_Download_Named_Credential__c; settings.eSignatureRestNamedCredential = row.ESignature_Rest_Named_Credential__c; settings.templateRootFolderHref = row.Template_Root_Folder_Href__c; settings.destinationRootFolderHref = row.Destination_Root_Folder_Href__c; settings.defaultTemplateDocumentHref = row.Default_Template_Document_Href__c; settings.defaultDocumentNamePrefix = row.Default_Destination_Document_Name_Prefix__c; settings.active = row.Active__c; return settings; } private static LetterSettings toLetterSettings(CLM_Letter_Definition__mdt row, AccountSettings account) { LetterSettings settings = new LetterSettings(); settings.accountCode = String.isNotBlank(row.Account_Code__c) ? row.Account_Code__c : account.accountCode; settings.letterCode = String.isNotBlank(row.Letter_Code__c) ? row.Letter_Code__c : 'APPRAISER_REVIEW'; settings.letterDisplayName = String.isNotBlank(row.Letter_Display_Name__c) ? row.Letter_Display_Name__c : row.DeveloperName; settings.description = row.Description__c; settings.isDefault = row.Is_Default__c; settings.sortOrder = row.Sort_Order__c; settings.templateRootFolderHref = firstNonBlankValue(row.Template_Root_Folder_Href__c, account.templateRootFolderHref); settings.destinationRootFolderHref = firstNonBlankValue(row.Destination_Root_Folder_Href__c, account.destinationRootFolderHref); settings.defaultTemplateDocumentHref = firstNonBlankValue(row.Default_Template_Document_Href__c, account.defaultTemplateDocumentHref); settings.defaultDocumentNamePrefix = firstNonBlankValue(row.Default_Destination_Document_Name_Prefix__c, account.defaultDocumentNamePrefix); settings.active = row.Active__c; return settings; } private static LetterSettings buildFallbackLetterSettings(AccountSettings account) { LetterSettings settings = new LetterSettings(); settings.accountCode = account.accountCode; settings.letterCode = 'APPRAISER_REVIEW'; settings.letterDisplayName = 'Appraiser Review Letter'; settings.description = 'Fallback current appraiser letter flow.'; settings.isDefault = true; settings.sortOrder = 10; settings.templateRootFolderHref = account.templateRootFolderHref; settings.destinationRootFolderHref = account.destinationRootFolderHref; settings.defaultTemplateDocumentHref = account.defaultTemplateDocumentHref; settings.defaultDocumentNamePrefix = account.defaultDocumentNamePrefix; settings.active = true; return settings; } private static String firstNonBlankValue(String preferredValue, String fallbackValue) { return String.isNotBlank(preferredValue) ? preferredValue : fallbackValue; } private static String buildDefaultDocumentName(String prefix, String caseNumber) { String normalizedPrefix = String.isNotBlank(prefix) ? prefix : 'Review'; return String.isNotBlank(caseNumber) ? normalizedPrefix + '_' + caseNumber + '.docx' : normalizedPrefix + '.docx'; } }