/*********************************************************************************************************** * Name : CA_RmPdfController * Purpose : Controller of CA_RmPdf Visualforce page **********************************************************************************************************/ public with sharing class CA_RmPdfController { private static final String RM_STATUS_NOT_ACQUIRED = 'Not Acquired'; private static final String RM_ACQUIRED_BY_PREVIOUS_OPP_SUFFIX = ' (previously acquired)'; private static final String DEFAULT_STATUS_COLOR = '#d9d9d9'; private static final Map STATUS_TO_COLOR = new Map{ CA_ServiceTrackerHelper.STATUS_APPROVED => '#c6dbb4', CA_ServiceTrackerHelper.STATUS_AUTOAPPROVED => '#c6dbb4', CA_ServiceTrackerHelper.STATUS_PENDING_APPROVAL => '#ecdca0', CA_ServiceTrackerHelper.STATUS_REJECTED => '#f4cccc', CA_ServiceTrackerHelper.STATUS_ACTION_REQUIRED => '#d9d2e9' }; private static final List RM_NOT_ACQUIRED_FIELDS = new List{ 'Reason_Right_Not_Acquired__c', 'Please_Expand__c', 'When_Is_Right_Available__c', 'Notes__c' }; private static final Set RM_BASE_SERVICE_QUERY_FIELDS = new Set{ 'Id', 'Master_Right__c', 'Master_Right__r.Name', 'Master_Right__r.Order__c', 'Master_Right__r.Approver__r.Name', 'Master_Right__r.Availability__c', 'Master_Right__r.Availability_Type__c', 'Approver_Name__c', 'Approval_Status__c', 'Acquired_Opportunity__c', 'Reason_Right_Not_Acquired__c', 'Please_Expand__c', 'When_Is_Right_Available__c', 'Notes__c' }; public Opportunity opp { get; set; } public Map fieldNameToLabelMap { get; set; } public Map fieldValuesMap { get; set; } public List serviceTables { get; set; } public static List ALL_FIELDS; public static final Map FIELD_REPLACEMENTS = new Map{ CA_Constants.RELATIONSHIP_TEAM_LEAD_FIELD => 'CA_RelationshipTeamLead__r.Name' }; public static List CLIENT_INFO_FIELDS = new List{ 'CA_Closer__c', 'FullLegalName__c', 'CA_MA_Label_ID__c', 'CA_MA_Label_Priority__c', 'CA_AssignedReviewer__c', CA_Constants.RELATIONSHIP_TEAM_LEAD_FIELD, 'CA_Deal_Type__c', 'CA_Company_Individual_Country__c', 'CA_ClientHasAssetsInOrder__c', 'CA_MA_Is_label_a_D3__c', 'CA_Current_or_expected_annual_revenue__c', 'CA_Genre__c', 'CA_Label_Artist_Description__c', 'CA_MA_Why_did_we_sign_this_label__c', 'CA_Key_Artists_and_or_Releases__c', 'CA_Day_to_Day_Contact_Name__c', 'CA_Day_to_Day_Contact_Email__c', 'CA_MA_Client_understanding_distribution__c' }; public static List CONTRACTUAL_INFO_FIELDS = new List{ 'CA_MA_Number_of_Releases_in_Catalog__c', 'CA_Deliverables__c', 'CA_Total_Advance_Amount__c', 'CA_Advance_Payment_Breakdown__c', 'CA_Projected_first_year_total_revenue__c', 'CA_MA_Is_there_a_marketing_drawdown_fund__c', 'CA_Digital_Distribution_fee__c', 'TermLengthInYears__c', 'CA_Special_Terms_Notes__c' }; public static List SERVICES_FIELDS = new List{ 'CA_Digital_Distribution_Territory__c', 'CA_Physical_Distribution_Territory__c', 'CA_MA_Service_restrictions__c', 'Mech_Admin__c', 'CA_MA_Current_distribution_deal_end_date__c', 'CA_Current_Distributor__c' }; static { ALL_FIELDS = new List(); ALL_FIELDS.addAll(CLIENT_INFO_FIELDS); ALL_FIELDS.addAll(CONTRACTUAL_INFO_FIELDS); ALL_FIELDS.addAll(SERVICES_FIELDS); ALL_FIELDS.addAll(FIELD_REPLACEMENTS.values()); } public List clientInfoFields { get { return CLIENT_INFO_FIELDS; } } public List contractualInfoFields { get { return CONTRACTUAL_INFO_FIELDS; } } public List servicesFields { get { return SERVICES_FIELDS; } } public CA_RmPdfController() { fieldNameToLabelMap = new Map(); fieldValuesMap = new Map(); serviceTables = new List(); Id opportunityId = ApexPages.currentPage().getParameters().get('oppId'); String query = 'SELECT Name, CurrencyIsoCode, StageName, CA_Main_Focus_Territory__c, ' + String.join(ALL_FIELDS, ',') + ' FROM Opportunity WHERE Id = :opportunityId'; opp = Database.query(query); Map> describeBySObject = SchemaUtils.getAllFieldsDescribeBySObjectNames( new List{ 'Opportunity' } ); Map oppFieldsMap = describeBySObject.get('Opportunity'); for (String fieldName : ALL_FIELDS) { if (FIELD_REPLACEMENTS.values().contains(fieldName)) { continue; } DescribeFieldResult fieldDesc = oppFieldsMap.get(fieldName); String formattedFieldValue; if (FIELD_REPLACEMENTS.containsKey(fieldName)) { List replacementPathParts = FIELD_REPLACEMENTS.get(fieldName).split('\\.'); Object fieldValue = opp.getSObject(replacementPathParts[0])?.get(replacementPathParts[1]); formattedFieldValue = String.valueOf(fieldValue ?? ''); } else { formattedFieldValue = FormatUtils.formatFieldsToString(opp, fieldName, fieldDesc.getType().toString()); } fieldValuesMap.put(fieldName, formattedFieldValue); fieldNameToLabelMap.put(fieldName, fieldDesc.getLabel()); } if (opp.StageName == CA_Constants.OPPORTUNITY_STAGE_ACTIVE_IN_OA) { buildServiceTables(); } } /** * Builds display-ready service tables for the PDF page by converting helper wrappers * into view wrappers with formatted values and status styles. */ private void buildServiceTables() { List helperTables = buildRmServiceTables(opp); for (RmServiceTableRowWrapper helperTable : helperTables) { ServiceTableRowWrapper table = new ServiceTableRowWrapper(); table.title = helperTable.title; table.status = helperTable.status; table.statusStyle = getStatusStyle(helperTable.status); table.hasRows = helperTable.hasRows; table.rows = new List(); for (RmServiceFieldRowWrapper helperRow : helperTable.rows) { table.rows.add( new ServiceFieldRowWrapper(helperRow.label, formatServiceTableValue(helperRow, opp.CurrencyIsoCode)) ); } serviceTables.add(table); } } /** * Builds Service Tracker service tables data for RM communication channels. * It loads setup metadata, queries related Opportunity_Right__c records, and * returns a display-ready structure containing table headers, statuses and rows. * * @param opp Opportunity used as context to load related services * @return List RM service table rows with formatted metadata references */ public static List buildRmServiceTables(Opportunity opp) { Id opportunityId = opp.Id; Map> describeBySObject = SchemaUtils.getAllFieldsDescribeBySObjectNames( new List{ 'Opportunity_Right__c', 'Opportunity' } ); Map serviceFieldsMap = describeBySObject.get('Opportunity_Right__c'); Map oppFieldsMap = describeBySObject.get('Opportunity'); Map> setupByRightName = CA_ServiceTrackerHelper.getFieldsFromSetup(); Set queryFields = new Set(RM_BASE_SERVICE_QUERY_FIELDS); for (String rightName : setupByRightName.keySet()) { for (CA_ServiceTrackerHelper.ServiceTrackerSetupFieldWrapper setupField : setupByRightName.get(rightName)) { addRmFieldToQuerySet(setupField.api, setupField.isOppField, queryFields, serviceFieldsMap, oppFieldsMap); } } List selectFields = new List(queryFields); List services = Database.query( 'SELECT ' + String.join(selectFields, ',') + ' FROM Opportunity_Right__c WHERE Opportunity__c = :opportunityId AND Master_Right__r.Active__c = TRUE' + ' ORDER BY Master_Right__r.Order__c' ); List serviceTables = new List(); String countryRegion = opp.CA_Main_Focus_Territory__c ?? opp.CA_Company_Individual_Country__c; for (Opportunity_Right__c service : services) { if (!CA_OpportunityTriggerHelper.isServiceAvailableForCountry(service, countryRegion)) { continue; } RmServiceTableRowWrapper table = new RmServiceTableRowWrapper(); table.title = service.Master_Right__r.Name; String baseStatus = service.Approval_Status__c ?? RM_STATUS_NOT_ACQUIRED; Id acquiredOpportunityId = service.Acquired_Opportunity__c; String acquiredSuffix = (acquiredOpportunityId != null && acquiredOpportunityId != opportunityId) ? RM_ACQUIRED_BY_PREVIOUS_OPP_SUFFIX : ''; table.status = baseStatus + acquiredSuffix; table.rows = new List(); if (baseStatus == RM_STATUS_NOT_ACQUIRED) { for (String fieldApiName : RM_NOT_ACQUIRED_FIELDS) { addRmFieldRow(table.rows, fieldApiName, service, serviceFieldsMap, false); } } else { String approverName = service.Master_Right__r?.Approver__r?.Name; if (baseStatus != CA_ServiceTrackerHelper.STATUS_AUTOAPPROVED && !String.isBlank(approverName)) { table.rows.add(new RmServiceFieldRowWrapper('Approver', approverName, null, null, null)); } List serviceSetup = setupByRightName.get( service.Master_Right__r.Name ); if (serviceSetup != null) { for (CA_ServiceTrackerHelper.ServiceTrackerSetupFieldWrapper setupField : serviceSetup) { String fieldApiName = setupField.api; if (!shouldRenderRmSetupField(setupField, service, serviceFieldsMap)) { continue; } if (setupField.isOppField) { addRmFieldRow(table.rows, fieldApiName, service.Opportunity__r, oppFieldsMap, true); } else { addRmFieldRow(table.rows, fieldApiName, service, serviceFieldsMap, false); } } } } table.hasRows = !table.rows.isEmpty(); serviceTables.add(table); } return serviceTables; } /** * Adds a setup field to the dynamic Opportunity_Right__c query field set. * It includes service-side fields directly and Opportunity fields using the * Opportunity__r relationship prefix. * * @param fieldApiName API name of the configured setup field * @param isOppField indicates whether the configured field belongs to Opportunity * @param queryFields set of fields used to build the dynamic SELECT clause * @param serviceFieldsMap describe map for Opportunity_Right__c * @param oppFieldsMap describe map for Opportunity */ private static void addRmFieldToQuerySet( String fieldApiName, Boolean isOppField, Set queryFields, Map serviceFieldsMap, Map oppFieldsMap ) { if (String.isBlank(fieldApiName)) { return; } if (isOppField) { if (oppFieldsMap.containsKey(fieldApiName)) { queryFields.add('Opportunity__r.' + fieldApiName); } return; } if (serviceFieldsMap.containsKey(fieldApiName)) { queryFields.add(fieldApiName); } else if (oppFieldsMap.containsKey(fieldApiName)) { queryFields.add('Opportunity__r.' + fieldApiName); } } /** * Evaluates whether a setup field should be rendered for a given service, * based on optional dependency field/value configuration. * * @param setupField Service Tracker setup field wrapper * @param service service record being processed * @param serviceFieldsMap describe map for Opportunity_Right__c * @return Boolean true when field should be rendered, otherwise false */ private static Boolean shouldRenderRmSetupField( CA_ServiceTrackerHelper.ServiceTrackerSetupFieldWrapper setupField, Opportunity_Right__c service, Map serviceFieldsMap ) { String dependantField = setupField.dependantField; if (String.isBlank(dependantField)) { return true; } Object dependantValue = serviceFieldsMap.containsKey(dependantField) ? service.get(dependantField) : null; if (dependantValue == null) { return false; } return String.valueOf(dependantValue) == setupField.dependantValue; } /** * Creates and appends a service table row wrapper from a source SObject field. * * @param rows target list to append row wrappers * @param fieldApiName API name of the source field * @param sourceRecord source record containing the field value * @param fieldsMap describe map for the source record type * @param isOppField flag indicating if source field belongs to Opportunity */ private static void addRmFieldRow( List rows, String fieldApiName, SObject sourceRecord, Map fieldsMap, Boolean isOppField ) { if (!fieldsMap.containsKey(fieldApiName)) { return; } DescribeFieldResult fieldDesc = fieldsMap.get(fieldApiName); Object fieldValue = sourceRecord.get(fieldApiName); rows.add( new RmServiceFieldRowWrapper( fieldDesc.getLabel(), fieldValue, fieldDesc.getType().toString(), fieldApiName, isOppField ) ); } /** * Formats service-table values using FormatUtils while preserving the original * source context (Opportunity or Opportunity_Right__c) from helper metadata. * * @param helperRow Service field wrapper from RM service-table builder * @param currencyIsoCode Opportunity currency used for currency formatting * @return String display-ready value for PDF and email rendering */ public static String formatServiceTableValue(RmServiceFieldRowWrapper helperRow, String currencyIsoCode) { if (helperRow.fieldType == null || String.isBlank(helperRow.fieldApiName)) { return String.valueOf(helperRow.value ?? ''); } SObject recordForFormat; if (helperRow.isOppField) { recordForFormat = new Opportunity(); } else { recordForFormat = new Opportunity_Right__c(); } recordForFormat.put(CurrencyUtils.CURRENCY_ISO_CODE, currencyIsoCode); recordForFormat.put(helperRow.fieldApiName, helperRow.value); return FormatUtils.formatFieldsToString(recordForFormat, helperRow.fieldApiName, helperRow.fieldType); } /** * Returns inline CSS style for status badge background color based on service status. * * @param status service status label * @return String inline CSS style declaration */ private String getStatusStyle(String status) { String backgroundColor = DEFAULT_STATUS_COLOR; if (status != null) { for (String statusPrefix : STATUS_TO_COLOR.keySet()) { if (status.startsWith(statusPrefix)) { backgroundColor = STATUS_TO_COLOR.get(statusPrefix); break; } } } return 'background-color:' + backgroundColor + ';'; } public class RmServiceTableRowWrapper { public String title { get; set; } public String status { get; set; } public Boolean hasRows { get; set; } public List rows { get; set; } } public class RmServiceFieldRowWrapper { public String label { get; set; } public Object value { get; set; } public String fieldType { get; set; } public String fieldApiName { get; set; } public Boolean isOppField { get; set; } public RmServiceFieldRowWrapper( String label, Object value, String fieldType, String fieldApiName, Boolean isOppField ) { this.label = label; this.value = value; this.fieldType = fieldType; this.fieldApiName = fieldApiName; this.isOppField = isOppField; } } public class ServiceTableRowWrapper { public String title { get; set; } public String status { get; set; } public String statusStyle { get; set; } public Boolean hasRows { get; set; } public List rows { get; set; } } public class ServiceFieldRowWrapper { public String label { get; set; } public String value { get; set; } public ServiceFieldRowWrapper(String label, String value) { this.label = label; this.value = value; } } }