function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
ChickenOrBeefChickenOrBeef 

Save both record page and embedded VisualForce page?

I have a VisualForce page embedded in my Opportunity layout. If a user makes changes to the Opportunity fields and then also makes changes in the embedded VisualForce page, they can't save both changes. They either have to save the Opportunity or save the VisualForce page, losing their changes to the other one.

Is there any way to let our users save both changes at the same time? For reference, see below for the VisualForce Page and Extension involved:


VisualForce Page:
<apex:page standardController="Opportunity" Extensions="ControllerContactSelectionOpportunity">
    
    <style>
        .added {
            background-color: #a4f3fc;
        }
        .removed {
            background-color: white;
        }
    </style>
    
    <apex:form >
    
        <apex:pageBlock >
            
            <apex:pageBlockTable  value="{!Records}" var="Con">
                
                <apex:column headerValue="Add To Opportunity" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.attach}"/>
                </apex:column>
                <apex:column headerValue="Name" value="{!Con.Name}" styleClass="{!IF(Con.attach == TRUE,'added','removed')}"/>
                <apex:column headerValue="Main User" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.mainUser}"/>
                </apex:column>
                <apex:column headerValue="General User" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.genUser}"/>
                </apex:column>
                <apex:column headerValue="Billing" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.billing}"/>
                </apex:column>
                <apex:column headerValue="E-Commerce" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.eComm}"/>
                </apex:column>
                <apex:column headerValue="Email" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.email}"/>
                </apex:column>
                <apex:column headerValue="CRM" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.crm}"/>
                </apex:column>
                <apex:column headerValue="Junior Decision Maker" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.junior}"/>
                </apex:column>
                <apex:column headerValue="Senior Decision Maker" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.senior}"/>
                </apex:column>
                <apex:column headerValue="Signature" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.signature}"/>
                </apex:column>
                <apex:column headerValue="Coach" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.coach}"/>
                </apex:column>
                <apex:column headerValue="Champion" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.champion}"/>
                </apex:column>
                <apex:column headerValue="I Don't Know" styleClass="{!IF(Con.attach == TRUE,'added','removed')}">
                    <apex:inputCheckbox value="{!Con.idk}"/>
                </apex:column>
                
            </apex:pageBlockTable>
            
            <Apex:pageBlockButtons location="bottom">
            
                <apex:commandButton action="{!customSave}" value="Save"/>
            
            </Apex:pageBlockButtons>
            
            <apex:outputPanel rendered="{!refreshPage}">
               <script>
                  window.top.location='/{!Opportunity.Id}';
               </script>
            </apex:outputPanel>
        
        </apex:pageBlock>
    
    </apex:form>
    
</apex:page>


Extension:
public class ControllerContactSelectionOpportunity{
    
    public class contactAttach{
        
        public string name {get;set;}
        public Boolean attach {get; set;}
        public Boolean mainUser {get; set;}
        public Boolean genUser {get; set;}
        public Boolean billing {get; set;}
        public Boolean eComm {get; set;}
        public Boolean email {get; set;}
        public Boolean crm {get; set;}
        public Boolean junior {get; set;}
        public Boolean senior {get; set;}
        public Boolean signature {get; set;}
        public Boolean coach {get; set;}
        public Boolean champion {get; set;}
        public Boolean idk {get; set;}
        public Contact con {get; set;}
        
    }
    
    public List<contactAttach> Records {get; set;}
    public Boolean refreshPage {get; set;}
    public Opportunity Record;
    public Set<String> roleContacts;
    public Map<String,OpportunityContactRole> roleMap;
    public Map<String,String> roleIDMap;
    
    public ControllerContactSelectionOpportunity(ApexPages.StandardController controller){
        
        Records = new List<contactAttach>();
        List<contactAttach> addedRecords = new List<contactAttach>();
        List<contactAttach> removedRecords = new List<contactAttach>();
        Record = (Opportunity) controller.getRecord();
        String AccountId = [SELECT AccountId FROM Opportunity WHERE Id = :Record.Id LIMIT 1].AccountId;
        roleContacts = new Set<String>();
        roleMap = new Map<String,OpportunityContactRole>();
        roleIDMap = new Map<String,String>();
        refreshPage = FALSE;
        
        FOR(OpportunityContactRole ocr : [SELECT
                                         	Id,
                                         	ContactId
                                          FROM
                                         	OpportunityContactRole
                                          WHERE
                                          	OpportunityId = :Record.Id]){
                                              
                                              roleContacts.add(ocr.ContactId);
                                              roleMap.put(ocr.ContactId,ocr);
                                              roleIDMap.put(ocr.ContactId,ocr.Id);
                                              
                                          }
        
        FOR(Contact c : [SELECT 
                             	Id, 
                             	Name,
                             	Main_User__c, 
                             	General_User__c,
                             	Billing__c,
                             	E_Commerce__c,
                             	Email__c,
                             	CRM__c,
                             	Junior_Decision_Maker__c,
                             	Senior_Decision_Maker__c,
                             	Signature__c,
                             	Coach__c,
                             	Champion__c,
                             	I_Don_t_Know__c
                             FROM 
                             	Contact 
                             WHERE 
                             	AccountId = :AccountId
                        	 ORDER BY Name ASC]){
                                    
                                    contactAttach ca = new contactAttach();
                                    ca.Name = c.Name;
                                    ca.attach = roleContacts.contains(c.Id) ? TRUE : FALSE;
                                    ca.mainUser = c.Main_User__c;
                                    ca.genUser = c.General_User__c;
                                    ca.billing = c.Billing__c;
                                    ca.eComm = c.E_Commerce__c;
                                    ca.email = c.Email__c;
                                    ca.crm = c.CRM__c;
                                    ca.junior = c.Junior_Decision_Maker__c;
                                    ca.senior = c.Senior_Decision_Maker__c;
                                    ca.signature = c.Signature__c;
                                    ca.coach = c.Coach__c;
                                    ca.champion = c.Champion__c;
                                    ca.idk = c.I_Don_t_Know__c;
                                    ca.con = c;
                                 
                                 IF(ca.attach == TRUE){
                                     
                                     addedRecords.add(ca);
                                     
                                 }
                                 ELSE{
                                     
                                     removedRecords.add(ca);
                                     
                                 }
            
        }
        
        FOR(contactAttach ar : addedRecords){
            
            Records.add(ar);
            
        }
        
        FOR(contactAttach rr : removedRecords){
            
            Records.add(rr);
            
        }
        
        
    }
    
    public PageReference CustomSave(){
        
        Set<String> contactsChanged = new Set<String>();
        List<Contact> contactsToUpdate = new List<Contact>();
        List<OpportunityContactRole> rolesToAdd = new List<OpportunityContactRole>();
        List<OpportunityContactRole> rolesToChange = new List<OpportunityContactRole>();
        List<OpportunityContactRole> rolesToDelete = new List<OpportunityContactRole>();
        
        FOR(contactAttach cna : Records){
                
            IF(cna.con.Main_User__c != cna.mainUser || cna.con.General_User__c != cna.genUser ||
               cna.con.Billing__c != cna.billing || cna.con.E_Commerce__c != cna.eComm ||
               cna.con.Email__c != cna.email || cna.con.CRM__c != cna.crm ||
               cna.con.Junior_Decision_Maker__c != cna.junior ||  cna.con.Senior_Decision_Maker__c != cna.senior ||
               cna.con.Signature__c != cna.signature || cna.con.Coach__c != cna.coach ||
               cna.con.Champion__c != cna.champion || cna.con.I_Don_t_Know__c != cna.idk){
                
                cna.con.Main_User__c = cna.mainUser;
                cna.con.General_User__c = cna.genUser;
                cna.con.Billing__c = cna.billing;
                cna.con.E_Commerce__c = cna.eComm;
                cna.con.Email__c = cna.email;
                cna.con.CRM__c = cna.crm;
                cna.con.Junior_Decision_Maker__c = cna.junior;
                cna.con.Senior_Decision_Maker__c = cna.senior;
                cna.con.Signature__c = cna.signature;
                cna.con.Coach__c = cna.coach;
                cna.con.Champion__c = cna.champion;
                cna.con.I_Don_t_Know__c = cna.idk;
                contactsToUpdate.add(cna.con);
                contactsChanged.add(cna.con.Id);
                
            }
            
            IF(cna.attach == TRUE && !roleContacts.contains(cna.con.Id)){
                
                OpportunityContactRole cr = new OpportunityContactRole();
                cr.ContactId = cna.con.Id;
                cr.OpportunityId = Record.Id;
                cr.Role = cna.mainUser == TRUE ? 'Main User' 
                    	: cna.genUser == TRUE ? 'General User' 
                    	: cna.billing == TRUE ? 'Billing' 
                    	: cna.eComm == TRUE ? 'E-Commerce' 
                    	: cna.email == TRUE ? 'Email' 
                    	: cna.crm == TRUE ? 'CRM' 
                    	: cna.junior == TRUE ? 'Junior' 
                    	: cna.senior == TRUE ? 'Senior' 
                    	: cna.signature == TRUE ? 'Signature' 
                    	: cna.coach == TRUE ? 'Coach' 
                    	: cna.champion == TRUE ? 'Champion' 
                    	: 'Other';
                rolesToAdd.add(cr);
                
            }
            ELSE IF(cna.attach == TRUE && roleContacts.contains(cna.con.Id) && contactsChanged.contains(cna.con.Id)){
                
                OpportunityContactRole cru = new OpportunityContactRole();
                cru.Id = roleIDMap.get(cna.con.Id);
                cru.Role = cna.mainUser == TRUE ? 'Main User' 
                    	 : cna.genUser == TRUE ? 'General User' 
                    	 : cna.billing == TRUE ? 'Billing' 
                    	 : cna.eComm == TRUE ? 'E-Commerce' 
                    	 : cna.email == TRUE ? 'Email' 
                    	 : cna.crm == TRUE ? 'CRM' 
                    	 : cna.junior == TRUE ? 'Junior' 
                    	 : cna.senior == TRUE ? 'Senior' 
                    	 : cna.signature == TRUE ? 'Signature' 
                    	 : cna.coach == TRUE ? 'Coach' 
                    	 : cna.champion == TRUE ? 'Champion' 
                    	 : 'Other';
                rolesToChange.add(cru);
                
            }
            ELSE IF(cna.attach == FALSE && roleContacts.contains(cna.con.Id)){
                
                rolesToDelete.add(roleMap.get(cna.con.Id));
                
            }
            
        }
        
        IF(!contactsToUpdate.isEmpty()){
            
            UPDATE contactsToUpdate;
            
        }
    
        IF(!rolesToAdd.isEmpty()){
            
            INSERT rolesToAdd;
            
        }
        
        IF(!rolesToChange.isEmpty()){
            
            UPDATE rolesToChange;
            
        }
        
        IF(!rolesToDelete.isEmpty()){
            
            DELETE rolesToDelete;
            
        }
        
        refreshPage = TRUE;
        RETURN null;
        
    }

}


Thanks,
Greg
Manuel EcheverriaManuel Echeverria
As far as I know when you embeed a Visualforce page into a Layout it's like showing two pages in one, but the actions are different. SO you have to either Save only on the visualforce page or in the Layout.

Have you try using the standard controller action {!save} on the Visualforce Page? That's the same action than the save button fron the layout.
ChickenOrBeefChickenOrBeef
Hey Manuel,

I tried using the default {!Save} action, but that didn't save either page.

I'm guessing the only way to achieve my goal would be to create a VisualForce page that includes the entire Opportunity and also whatever's in my embedded VisualForce page, so that everything is in one VisualForce page?

Thanks,
Greg
Manuel EcheverriaManuel Echeverria
Yes I'm afraid that's the only way. You will have to create your own Visualforce page with everything on it and use as the default layout for your object and record. Just check wether you will use it as well for new and edit records.  Setup -> Objects -> your_object__c ->
Buttons, Links, and Actions -> Edit and New actios.

I hope it helps you.