/** * @description Builds CLM-ready JSON payload for Appraiser Case with related Deficiencies. * Used to transform Salesforce data into DocuSign CLM merge field structure. */ public class AppraiserCasePayloadBuilder { /** * @description Generates CLM merge payload for a given Appraiser Case. * @param caseId Appraiser_Case__c record Id * @return Map CLM merge data ready for template rendering */ public static Map buildPayload(String caseId) { // Query parent case with all child deficiencies Appraiser_Case__c appraiserCase = queryAppraiserCase(caseId); if (appraiserCase == null) { throw new IllegalArgumentException('Appraiser Case not found: ' + caseId); } // Build CLM payload structure Map payload = new Map(); payload.put('AppraiserCaseNumber', appraiserCase.Name); payload.put('AppraiserFieldReviewDate', formatDate(appraiserCase.Appraiser_Field_Review_Date__c)); payload.put('LetterSentDate', formatDate(appraiserCase.Letter_Sent_Date__c)); payload.put('FHACaseNumber', appraiserCase.FHA_Case_Number__c); payload.put('AppraiserName', appraiserCase.Appraiser_Name__c); payload.put('AppraiserSalutation', appraiserCase.Appraiser_Salutation__c); payload.put('AppraiserLastName', appraiserCase.Appraiser_Last_Name__c); payload.put('AppraiserEmail', appraiserCase.Appraiser_Email__c); payload.put('AppraiserStreet', appraiserCase.Appraiser_Street__c); payload.put('AppraiserCity', appraiserCase.Appraiser_City__c); payload.put('AppraiserStateProvince', appraiserCase.Appraiser_State_Province__c); payload.put('AppraiserPostalCode', appraiserCase.Appraiser_Postal_Code__c); payload.put('AppraiserCountry', appraiserCase.Appraiser_Country__c); payload.put('AppraiserAddress', formatMailingAddress( appraiserCase.Appraiser_Street__c, appraiserCase.Appraiser_City__c, appraiserCase.Appraiser_State_Province__c, appraiserCase.Appraiser_Postal_Code__c, appraiserCase.Appraiser_Country__c )); payload.put('PropertyStreet', appraiserCase.Property_Street__c); payload.put('PropertyCity', appraiserCase.Property_City__c); payload.put('PropertyStateProvince', appraiserCase.Property_State_Province__c); payload.put('PropertyPostalCode', appraiserCase.Property_Postal_Code__c); payload.put('PropertyCountry', appraiserCase.Property_Country__c); payload.put('PropertyAddress', formatAddress(appraiserCase)); // Transform child deficiencies into DeficiencyList array List> deficiencyList = new List>(); if (appraiserCase.Deficiencies__r != null && !appraiserCase.Deficiencies__r.isEmpty()) { for (Appraiser_Case_Deficiency__c deficiency : appraiserCase.Deficiencies__r) { Map defMap = new Map(); defMap.put('deficiencyNumber', deficiency.Deficiency_Number__c); defMap.put('description', deficiency.Description__c); defMap.put('resolution', deficiency.Resolution__c); defMap.put('reference', deficiency.Reference__c); deficiencyList.add(defMap); } } payload.put('DeficiencyList', deficiencyList); return payload; } /** * @description Returns CLM payload as JSON string for API transmission. * @param caseId Appraiser_Case__c record Id * @return String JSON representation of payload */ public static String buildPayloadJson(String caseId) { Map payload = buildPayload(caseId); return JSON.serialize(payload); } /** * @description Query Appraiser Case with related Deficiencies ordered by number. * @param caseId Appraiser_Case__c record Id * @return Appraiser_Case__c Record with Deficiencies__r populated */ private static Appraiser_Case__c queryAppraiserCase(String caseId) { List results = [ SELECT Id, Name, Appraiser_Field_Review_Date__c, Letter_Sent_Date__c, FHA_Case_Number__c, Appraiser_Name__c, Appraiser_Salutation__c, Appraiser_Last_Name__c, Appraiser_Email__c, Appraiser_Street__c, Appraiser_City__c, Appraiser_State_Province__c, Appraiser_Postal_Code__c, Appraiser_Country__c, Property_Street__c, Property_City__c, Property_State_Province__c, Property_Postal_Code__c, Property_Country__c, (SELECT Id, Deficiency_Number__c, Description__c, Resolution__c, Reference__c FROM Deficiencies__r ORDER BY Deficiency_Number__c ASC) FROM Appraiser_Case__c WHERE Id = :caseId LIMIT 1 ]; return results.isEmpty() ? null : results.get(0); } /** * @description Format date for CLM merge (for example Apr 09, 2026) or null. * @param dt Date field value * @return String Formatted date or null */ private static String formatDate(Date dt) { return dt != null ? DateTime.newInstance(dt, Time.newInstance(0, 0, 0, 0)).format('MMM dd, yyyy') : null; } private static String formatAddress(Appraiser_Case__c appraiserCase) { return formatMailingAddress( appraiserCase.Property_Street__c, appraiserCase.Property_City__c, appraiserCase.Property_State_Province__c, appraiserCase.Property_Postal_Code__c, appraiserCase.Property_Country__c ); } public static String formatMailingAddress( String street, String city, String stateProvince, String postalCode, String country ) { List lines = new List(); if (String.isNotBlank(street)) { lines.add(street.trim()); } List localityParts = new List(); if (String.isNotBlank(city)) { localityParts.add(city.trim()); } if (String.isNotBlank(stateProvince)) { localityParts.add(stateProvince.trim()); } String locality = String.join(localityParts, ', '); if (String.isNotBlank(postalCode)) { locality = String.isNotBlank(locality) ? locality + ' ' + postalCode.trim() : postalCode.trim(); } if (String.isNotBlank(locality)) { lines.add(locality); } if (String.isNotBlank(country)) { lines.add(country.trim()); } return lines.isEmpty() ? null : String.join(lines, ', '); } }