im new in salesforce programming.. I made an pdf report (button) on opportunity.. I can get the opportunity object field value (and opportunityLineItem) and convert it to pdf.. but down below the opportunity page, there`s approval history and stage history section.. how do i get those value?

enter image description here

this is my class (still cant figure it out how to get the history) :

public class pdf_opportunity {
public Opportunity opp {get;set;}
public List<OpportunityLineItem> listProduct {get;set;} 
public List<wrapper> wrp {get;set;}
public String idsf {get;set;}
Public Integer count{get;set;}
private ApexPages.StandardController std;

public pdf_opportunity(ApexPages.StandardController controller){

public void pdf_opportunity2()

    if(test.isRunningTest()==false) idsf = ApexPages.currentPage().getParameters().get('id');
    opp = [SELECT id, Lead__r.Name, Opportunity_Group__r.Name, Last_Stage__c, Account.Name, Branch__c, Named_User__c,Named_User__r.First_Name__c,Status__c, Name, Type, Description, CampaignId, OwnerId, Owner.Name,
           CloseDate, StageName, Probability, Customer_Visit__c, Interest__c, Amount, Start_Date__c, Interval__c, Nomor_Kerjasama__c, Contract_No__c, Contract_Start_Date__c,
           Contract_Break__c, Contract_End_Date__c, Note_Step_1_GM__c, Note_Step_2_Direksi__c, Note_Step_3_Komisaris__c, Note_Step_4_RUPS__c, NextStep, Cancelled_Reason__c,
           CreatedById, LastModifiedById FROM Opportunity WHERE id =:idsf];

    listProduct = [SELECT id, Product2.Name, Quantity, UnitPrice, ServiceDate, Description, ListPrice 
                   FROM OpportunityLineItem WHERE OpportunityId = :opp.Id];

    wrp = new List<wrapper>(); 
    for(OpportunityLineItem oppL : listProduct){
        wrapper wp=new wrapper();
        wp.oppChild = oppL;


public class wrapper{
    Public integer cou {get;set;}
    Public OpportunityLineItem oppChild{get;set;}



Strictly speaking, you don't need a controller at all; you'd use one only if you need to order the values in some manner other than the default.

<apex:page standardController="Opportunity">
    <apex:dataTable value="{!Opportunity.Histories}" var="history">
        <apex:column value="{!history.Field}" />
    <apex:dataTable value="{!Opportunity.ProcessInstances}" var="process">
        <apex:column value="{!process.Status}" />

As a general rule of thumb, you can typically use a subquery to get child records if you need to:

opp = [SELECT (SELECT Field FROM Histories), (SELECT Status FROM ProcessInstances), ... 
       FROM Opportunity WHERE Id = :std.getId()];

Generally speaking, you should avoid writing a controller for trivial purposes unless you need to; the StandardController can query most of the data for you if you ask. You should consider using subqueries when possible, if you need the controller at all. This will simplify your code to the bare minimum. However, the only down side is that this is limited to ~1,000 rows of data (actual maximums depend on system-determined factors), but since repeating elements have this limit already, you should be okay in most cases.

You might want to take a moment to read more about Relationship Queries and the StandardController class.

  • so opportunity.Histories = ProcessInstanceHistory .. and ProcessInstanceWorkitems = ???? .. I type Opportunity.Workitems but error there`s no such field on opportunity – Arief Gunawan Tjiptorahardja Jul 8 '18 at 6:53
  • @AriefGunawanTjiptorahardja No, Histories is OpportunityFieldHistory, ProcessSteps is ProcessInstanceHistory, ProcessInstances is ProcessInstance. Workitems are a child of ProcessInstance; if you need those, you'll have to query them separately. – sfdcfox Jul 8 '18 at 17:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.