I have had many requests in the past to allow for quick easy updates to a related list. You can use list views to do some of the updates, but there is no way to do a quick update to a related list aside from installing an app. GridBuddy is a great app that can accomplish this at a very reasonable price.
However I recently had a request to do one simple update on Quotes that are related to an Opportunity, just one checkbox and everything else would be read only. Below is the source code for the Visualforce Page, Custom Controller and Test Class.
There are a couple custom fields mixed in here but most of this is standard Salesforce. This would be a Visualforce page that would be placed on the Opportunity page layout and it would show related Quotes with the option to update and save changes.
VF Page:
<apex:page standardController="Opportunity" extensions="quoteController">
<apex:outputPanel id="refresh" rendered="true">
<apex:outputPanel id="refresh1" rendered="{!refreshPage}">
<script>
window.top.location='/{!Opportunity.id}';
</script>
</apex:outputPanel>
</apex:outputPanel>
<style>
.pbBody td {
border-color: #E3DEB8;
border-style: none none solid;
border-width: medium medium 2px;
padding-bottom: 4px;
padding-top: 4px;
width: 85px;
}
.pbBody input { width: 105px;}
.pbBody .nameColumn { width: 125px;}
.hdr {;}
</style>
<apex:form >
<apex:messages />
<apex:pageBlock title="Edit Quotes">
<apex:pageBlockButtons >
<apex:commandButton action="{!saveChanges}" value="Save" status="ajaxStatus" />
</apex:pageBlockButtons>
<apex:actionStatus id="ajaxStatus" startText="Updating quotes...">
<apex:facet name="stop">
<apex:outputPanel id="main">
<table>
<tr>
<apex:repeat value="{!headers}" var="h">
<td class="hdr">{!h}</td>
</apex:repeat>
</tr>
<apex:repeat value="{!Quotes}" var="q">
<tr>
<td><apex:outputField value="{!q.QuoteNumber}" /></td>
<td><apex:outputField value="{!q.Name}" /></td>
<td><apex:outputField value="{!q.IsSyncing}" /></td>
<td><apex:inputField value="{!q.Price__c}" /></td>
<td><apex:inputField value="{!q.Margin__c}" /></td>
</tr>
</apex:repeat>
</table>
</apex:outputPanel>
</apex:facet>
</apex:actionStatus>
</apex:pageBlock>
</apex:form>
</apex:page>
Custom Apex Controller:
public class quoteController {
public Boolean refreshPage {get; set;}
// Constructor
public quoteController (ApexPages.StandardController controller) {
refreshPage=false;
this.opp = (Opportunity)controller.getSubject();
this.quote = [ SELECT
q.Id, q.QuoteNumber, q.name, q.IsSyncing, q.Price__c,
q.Margin__c
FROM
Quote q
WHERE
q.OpportunityID = :opp.id ];
}
// Action Method called from page button
public pagereference saveChanges() {
update this.quote;
refreshPage=true;
return null;
}
// Action Method called from page link
public pagereference newQuote() {
Quote q = new Quote();
q.OpportunityID =this.opp.id;
quote.add( q );
return null;
}
// public Getter to provide table headers
public string[] getheaders() { return new string []
{'Quote Number','Quote Name','Synching','Price',
'Margin','Total Margin' } ; }
// public Getter to list project deliverables
public Quote[] getQuotes() {
return this.quote;
}
// class variables
Opportunity opp;
Quote[] quote;
}
Test Class:
@isTest
private class controllerTest {
public static testMethod void testMyController() {
Account a = new Account(name='Test Account');
insert a;
Opportunity opp = new Opportunity();
opp.Name = 'Test Opp';
opp.Amount = 100;
opp.CloseDate = Date.today();
opp.Probability = 10;
opp.StageName = 'New';
insert opp;
// start the test execution context
Test.startTest();
// set the test's page to your VF page (or pass in a PageReference)
Test.setCurrentPage(Page.OpportunityQuoteGrid);
// call the constructor
quoteController controller = new quoteController(new ApexPages.StandardController(opp));
//quoteController controller = new quoteController();
//String nextPage = controller.save().getUrl();
// test action methods on your controller and verify the output with assertions
controller.saveChanges();
controller.getHeaders();
controller.newQuote();
controller.getQuotes();
// stop the test
Test.stopTest();
}
}