• Sammy7
  • NEWBIE
  • 125 Points
  • Member since 2016

  • Chatter
    Feed
  • 0
    Best Answers
  • 2
    Likes Received
  • 0
    Likes Given
  • 22
    Questions
  • 37
    Replies
Hi, 
 So I have an input field :<b>To: </b> <apex:inputfield value="{!q.contactid}"/><p/>

and in my controller, I have:
public Quote q {get;set;}

and then I would like to present the email address in this outputfield: <b>NAME: </b> <apex:outputtext value="{!q.contact.name}"/><p/> 

However this is not working, Please help: 
Below is my VF page: 
<apex:page standardController="Quote" extensions="email_class">
    <apex:form >
        <apex:pageBlock title="Email Details" id="ed">
            <b>To: </b> <apex:inputfield value="{!q.contactid}"/><p/>
         <apex:actionSupport event="onchange"  action="{!Contactpopulated}" rerender="ed"/>      
               <b>NAME: </b> <apex:outputtext value="{!q.contact.email}"/><p/> 
.......
.......

and my extension:
public class email_class{
        
    Public string ToAddresses {get;set;}
    Public string CCAddresses {get;set;}
    Public string quoteId {get;set;}
    Public string subject {get;set;}
    public string email_body {get;set;}
    public string emailTo {get;set;}
    public string emailCC {get;set;}
   public Quote q {get;set;}
   // public Contact ct {get;set;}
     public  string [] ccaddress; 
       
        public email_class(ApexPages.StandardController controller) {
                quoteId = ApexPages.currentPage().getParameters().get('id');
              Quote  q= (Quote)controller.getRecord();
    }

             public void Contactpopulated(){
       q.contact=[ Select  email, name From contact where  id=:q.contactid limit 1]; 
}


 
  • March 09, 2017
  • Like
  • 0
I can excute this fine in excute anonymous, but when I put it in the class, I get "Unexpected token: '='" at LINE 5
 
public class testProduct {
    
    public Product2 shipping {get;set;} 
    
    shipping = [SELECT id, name, productcode from PRODUCT2 where productcode='shipping' limit 1];

 
  • February 27, 2017
  • Like
  • 0
I dont get this error...I tried retyping the quotes. Please help:


System.QueryException: line 1:282 no viable alternative at character '%'
Class.opportunityProductEntryExtension.updateAvailableList: line 119, column 1
Class.opportunityProductEntryTests.theTests: line 53, column 1


 
public with sharing class opportunityProductEntryExtension {

    public Opportunity theOpp {get;set;}
    public String searchString {get;set;}
    public opportunityLineItem[] shoppingCart {get;set;}
    public priceBookEntry[] AvailableProducts {get;set;}
    public Pricebook2 theBook {get;set;}   
    
    public String toSelect {get; set;}
    public String toUnselect {get; set;}
    public Decimal Total {get;set;}
    
    public Boolean overLimit {get;set;}
    public Boolean multipleCurrencies {get; set;}
    
    private Boolean forcePricebookSelection = false;
    
    private opportunityLineItem[] forDeletion = new opportunityLineItem[]{};


    public opportunityProductEntryExtension(ApexPages.StandardController controller) {

        // Need to know if org has multiple currencies enabled
        multipleCurrencies = UserInfo.isMultiCurrencyOrganization();

        // Get information about the Opportunity being worked on
        if(multipleCurrencies)
            theOpp = database.query('select Id, Pricebook2Id, Pricebook2.Name, CurrencyIsoCode from Opportunity where Id = \'' + controller.getRecord().Id + '\' limit 1');
        else
            theOpp = [select Id, Pricebook2Id, PriceBook2.Name from Opportunity where Id = :controller.getRecord().Id limit 1];
        
        // If products were previously selected need to put them in the "selected products" section to start with
        shoppingCart = [select Id, Quantity, TotalPrice, UnitPrice, Description, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.PriceBook2Id from opportunityLineItem where OpportunityId=:theOpp.Id];

        // Check if Opp has a pricebook associated yet
        if(theOpp.Pricebook2Id == null){
            Pricebook2[] activepbs = [select Id, Name from Pricebook2 where isActive = true limit 2];
            if(activepbs.size() == 2){
                forcePricebookSelection = true;
                theBook = new Pricebook2();
            }
            else{
                theBook = activepbs[0];
            }
        }
        else{
            theBook = theOpp.Pricebook2;
        }
        
        if(!forcePricebookSelection)
            updateAvailableList();
    }
    
    // this is the 'action' method on the page
    public PageReference priceBookCheck(){
    
        // if the user needs to select a pricebook before we proceed we send them to standard pricebook selection screen
        if(forcePricebookSelection){        
            return changePricebook();
        }
        else{
        
            //if there is only one active pricebook we go with it and save the opp
            if(theOpp.pricebook2Id != theBook.Id){
                try{
                    theOpp.Pricebook2Id = theBook.Id;
                    update(theOpp);
                }
                catch(Exception e){
                    ApexPages.addMessages(e);
                }
            }
            
            return null;
        }
    }
       
    public String getChosenCurrency(){
    
        if(multipleCurrencies)
            return (String)theOpp.get('CurrencyIsoCode');
        else
            return '';
    }

    public void updateAvailableList() {
    
        // We dynamically build a query string and exclude items already in the shopping cart
        String qString = 'select Id, Pricebook2Id, IsActive, Product2.Name, Product2.ProductCode, Product2.Family, Product2.IsActive, Product2.Description, UnitPrice from PricebookEntry where IsActive=true and Pricebook2Id = \'' + theBook.Id + '\'';
        if(multipleCurrencies)
            qstring += 'and CurrencyIsoCode = \'' + theOpp.get('currencyIsoCode') + '\'';
        
        // note that we are looking for the search string entered by the user in the name OR description
        // modify this to search other fields if desired
        if(searchString!=null){
            qString+= 'and (Product2.Name like \'%' + searchString + '%\' or Product2.ProductCode like \'%' + searchString + '%\')';
        }
        
        Set<Id> selectedEntries = new Set<Id>();
        for(opportunityLineItem d:shoppingCart){
            selectedEntries.add(d.PricebookEntryId);
        }
        
        if(selectedEntries.size()>0){
            String tempFilter = ' and Id not in (';
            for(Id i : selectedEntries){
                tempFilter+= '\'' + (String)i + '\',';
            }
            String extraFilter = tempFilter.substring(0,tempFilter.length()-1);
            extraFilter+= ')';
            
            qString+= extraFilter;
        }
        
        qString+= ' order by Product2.Name';
        qString+= ' limit 101';
        
        system.debug('qString:' +qString);        
        AvailableProducts = database.query(qString);
        
        // We only display up to 100 results... if there are more than we let the user know (see vf page)
        if(AvailableProducts.size()==101){
            AvailableProducts.remove(100);
            overLimit = true;
        }
        else{
            overLimit=false;
        }
    }
    
    public void addToShoppingCart(){
    
        // This function runs when a user hits "select" button next to a product
    
        for(PricebookEntry d : AvailableProducts){
            if((String)d.Id==toSelect){
                shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, UnitPrice=d.UnitPrice));
                break;
            }
        }
        
        updateAvailableList();  
    }
    

    public PageReference removeFromShoppingCart(){
    
        // This function runs when a user hits "remove" on an item in the "Selected Products" section
    
        Integer count = 0;
    
        for(opportunityLineItem d : shoppingCart){
            if((String)d.PriceBookEntryId==toUnselect){
            
                if(d.Id!=null)
                    forDeletion.add(d);
            
                shoppingCart.remove(count);
                break;
            }
            count++;
        }
        
        updateAvailableList();
        
        return null;
    }
    
    public PageReference onSave(){
    
        // If previously selected products are now removed, we need to delete them
        if(forDeletion.size()>0)
            delete(forDeletion);
    
        // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here
        try{
            if(shoppingCart.size()>0)
                upsert(shoppingCart);
        }
        catch(Exception e){
            ApexPages.addMessages(e);
            return null;
        }  
           
        // After save return the user to the Opportunity
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }
    
    public PageReference onCancel(){
 
        // If user hits cancel we commit no changes and return them to the Opportunity   
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }
    
    public PageReference changePricebook(){
    
        // This simply returns a PageReference to the standard Pricebook selection screen
        // Note that is uses retURL parameter to make sure the user is sent back after they choose
    
        PageReference ref = new PageReference('/oppitm/choosepricebook.jsp');
        ref.getParameters().put('id',theOpp.Id);
        ref.getParameters().put('retURL','/apex/opportunityProductEntry?id=' + theOpp.Id);
        
        return ref;
    }
}


 
@istest
private class opportunityProductEntryTests {

    static testMethod void theTests(){
        
        // You really should create test data, but I'm going to query instead
        // It's my best shot of avoiding a test failure in most orgs
        // Once you've installed this package though, you might want to write your own tests
        // or at least customize these ones to make them more applicable to your org
            
        OpportunityLineItem oli = [select Id, PricebookEntryId, PricebookEntry.Pricebook2Id, PricebookEntry.Name, PriceBookEntry.Product2Id, OpportunityId, Opportunity.AccountId from OpportunityLineItem limit 1];
               
                
        ////////////////////////////////////////
        //  test opportunityProductEntry
        ////////////////////////////////////////
        
        // load the page       
        PageReference pageRef = Page.opportunityProductEntry;
        pageRef.getParameters().put('Id',oli.OpportunityId);
        Test.setCurrentPageReference(pageRef);
        
        // load the extension
        opportunityProductEntryExtension oPEE = new opportunityProductEntryExtension(new ApexPages.StandardController(oli.Opportunity));
        
        // test 'getChosenCurrency' method
        if(UserInfo.isMultiCurrencyOrganization())
            System.assert(oPEE.getChosenCurrency()!='');
        else
            System.assertEquals(oPEE.getChosenCurrency(),'');

        // we know that there is at least one line item, so we confirm
        Integer startCount = oPEE.ShoppingCart.size();
        system.assert(startCount>0);

        //test search functionality without finding anything
        oPEE.searchString = 'michaelforce is a hip cat';
        oPEE.updateAvailableList();
        system.assert(oPEE.AvailableProducts.size()==0);
        
        //test remove from shopping cart
        oPEE.toUnselect = oli.PricebookEntryId;
        oPEE.removeFromShoppingCart();
        system.assert(oPEE.shoppingCart.size()==startCount-1);
        
        //test save and reload extension
        oPEE.onSave();
        oPEE = new opportunityProductEntryExtension(new ApexPages.StandardController(oli.Opportunity));
        system.assert(oPEE.shoppingCart.size()==startCount-1);
        
        // test search again, this time we will find something
        oPEE.searchString = oli.PricebookEntry.Name;
        oPEE.updateAvailableList();
        system.assert(oPEE.AvailableProducts.size()>0);       

        // test add to Shopping Cart function
        oPEE.toSelect = oPEE.AvailableProducts[0].Id;
        oPEE.addToShoppingCart();
        system.assert(oPEE.shoppingCart.size()==startCount);
                
        // test save method - WITHOUT quanitities and amounts entered and confirm that error message is displayed
        oPEE.onSave();
        system.assert(ApexPages.getMessages().size()>0);
       
        opEE.addtoshoppingcart();
        
        // add required info and try save again
        for(OpportunityLineItem o : oPEE.ShoppingCart){
            o.quantity = 5;
            o.unitprice = 300;
        }
        oPEE.onSave();
        
        // query line items to confirm that the save worked
        opportunityLineItem[] oli2 = [select Id from opportunityLineItem where OpportunityId = :oli.OpportunityId];
        system.assert(oli2.size()==startCount);
        
        // test on new Opp (no pricebook selected) to make sure redirect is happening
        Opportunity newOpp = new Opportunity(Name='New Opp',stageName='Pipeline',Amount=10,closeDate=System.Today()+30,AccountId=oli.Opportunity.AccountId);
        insert(newOpp);
        oPEE = new opportunityProductEntryExtension(new ApexPages.StandardController(newOpp));
        System.assert(oPEE.priceBookCheck()!=null);
        
        // final quick check of cancel button
        System.assert(oPEE.onCancel()!=null);
        
        
        ////////////////////////////////////////
        //  test redirect page
        ////////////////////////////////////////
        
        // load the page
        pageRef = Page.opportunityProductRedirect;
        pageRef.getParameters().put('Id',oli2[0].Id);
        Test.setCurrentPageReference(pageRef);

        // load the extension and confirm that redirect function returns something
        opportunityProductRedirectExtension oPRE = new opportunityProductRedirectExtension(new ApexPages.StandardController(oli2[0]));
        System.assert(oPRE.redirect()!=null);
     
    }
}

 
  • February 07, 2017
  • Like
  • 0
Hi, 
 Im getting the following error and cannot figure out why: 

System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_EMAIL_ADDRESS, Email address is invalid: null: [toAddresses, null]

Class.email_class.send: line 48, column 1
Class.TestquoteEmailclass.sendEmailTestMethod: line 47, column 1

Below is my test class: 
@isTest
public class TestquoteEmailclass {
    @isTest static void sendEmailTestMethod(){
    
        Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
        insert prod;
        PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId(), IsActive= true); 
        insert pbe; 
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity (name='Testopp', Accountid=acc.id, CloseDate= date.today(), StageName='Closed Won', Pricebook2id=Test.getStandardPricebookId());
        insert opp; 
        OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
        insert oppLine;       
        Quote q= new Quote (Name='Testq', Opportunityid=opp.id, QuotetoInvoice__c= True,  Pricebook2id=Test.getStandardPricebookId());
        insert q;
    
     System.currentPagereference().getParameters().put('id',q.id);
     Attachment objAtt = new Attachment();
     objAtt.Name = 'Test';
     objAtt.body = Blob.valueof('string');
     objAtt.ParentId = q.Id;
     insert objAtt;

         ApexPages.StandardController sc =new ApexPages.StandardController(q); 
        email_class ec=new email_class (sc);
        ec.ToAddresses='test@gmail.com';
         ec.CCAddresses='test2@gmail.com';
        ec.emailCC= 'test3@gmail.com';
        ec.email_body='test1111111';
        ec.subject='test22222';
        
         Messaging.InboundEmail email = new Messaging.InboundEmail();  
         Messaging.InboundEnvelope env = new Messaging.InboundEnvelope();
        email.plainTextBody = 'This should become a note';
        email.fromAddress ='test@test.com';
        String contactEmail = 'jsmith@salesforce.com';
        email.ccAddresses = new String[] {'Jon Smith <' + contactEmail + '>'};
        email.subject = 'Dummy Account Name 123';

        ec.send();

}
}

And my controller:
public class email_class{
        
    Public string ToAddresses {get;set;}
    Public string CCAddresses {get;set;}
    Public string quoteId {get;set;}
    Public string subject {get;set;}
    public string email_body {get;set;}
    public string emailTo {get;set;}
    public string emailCC {get;set;}
     public  string [] ccaddress;   
              
        
        
        public email_class(ApexPages.StandardController controller) {
                quoteId = ApexPages.currentPage().getParameters().get('id');
    }
        
    Public PageReference send(){

                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); // set the to address
                mail.setToAddresses(new String[] {emailTo}); 
               // mail.setCCAddresses(new String[] {emailCC});   //set the cc address
                   
        if(emailCC != null && emailCC.trim() != '')
        {
        ccaddress = emailCC.split(',',0);
        mail.setCCAddresses(ccaddress);}
                                                              
   
                mail.setSubject(subject);
                mail.setBccSender(false);
                mail.setUseSignature(false);
                mail.setPlainTextBody(email_body);
               

                List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();

                for (Attachment a : [select Name, Body, BodyLength from Attachment where ParentId = :quoteId]){  // Add to attachment file list  
                        Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();  
                        efa.setFileName(a.Name); 
                        efa.setBody(a.Body); 
                        fileAttachments.add(efa);
                }
                mail.setFileAttachments(fileAttachments);// Send email
                Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
                return null;
        }
}

Any help is appreciated !
  • January 26, 2017
  • Like
  • 1
Hi, 
 I need the ability to send an email from a quote detail page that automatically attaches the file (only one file) under "Notes&Attachment" section.
So there are two ways I envision this: 
1) A button that sends email (gets address from "Email" field under Quote detail page) and attaches the file
2) A button that opens up the standard email page with the attachment already attached and I can lookup the contact and write the message and send an email

I have no idea which is easier and what are the steps to accomplish this, so any guidance is appreciated. 
Thanks.
  • January 25, 2017
  • Like
  • 0
Hi, 
 I overrode the "Createpdf" button on quote page with a custom VF page (Quotecustom). I would like to create another button on quote page that lets me save that pdf as an attachment to the QuotePDF related list. 
What are the steps to do this? 
I know I need a controller that looks something like this: 
public class attachPDFToquote {
	
	private final quote q; //quote object
	
	//constructor
	public attachPDFToquote(ApexPages.StandardController standardPageController) {
		q = (Quote)standardPageController.getRecord(); //instantiate the quote object for the current record
	}
	
	//method called from the Visualforce's action attribute
	public PageReference attachPDF() {
		//generate and attach the PDF document
		PageReference pdfPage = Page.quotecustom; //create a page reference to quote custom Visualforce page
		Blob pdfBlob = pdfPage.getContent(); //get the output of the page, as displayed to a user in a browser
		Attachment attach = new Attachment(parentId = q.Id, Name = 'pdfAttachmentDemo.pdf', body = pdfBlob); //create the attachment object
		insert attach; //insert the attachment
		//redirect the user
		PageReference pageWhereWeWantToGo = new ApexPages.StandardController(q).view(); //we want to redirect the User back to the quote detail page
		pageWhereWeWantToGo.setRedirect(true); //indicate that the redirect should be performed on the client side
		return pageWhereWeWantToGo; //send the User on their way
	}

}
Im confused about the next step...do I have to create another seperate VF page or add the controller as an "extension" to my Quotecustom VF page. Please excuse my misunderstandings as Im new to this. 

THanks.
 
  • December 05, 2016
  • Like
  • 1
So I have custom object (invoice__c) with master-detail to opportunity. I have a custom field (AF_LastSyncTime__c ) on invoice__c that Im trying to access.
so here is my query:
for (Opportunity opp : [select Id, (select id, AF_LastSyncTime__c from Invoices__r),
                       (select Id, Invoice_No__c, QuotetoInvoice__c, CreatedDate
                         from Quotes 
                         order by CreatedDate DESC)
                  from Opportunity
                  where Id IN :oppsId]) 
{

 if (opp.Invoices__r.AF_LastSyncTime__c==null) return 'blah blah blah'; 

 }

No error with query, but I get invalid foreign key relationship in the if statement....why is that?

Thanks.
  • November 01, 2016
  • Like
  • 0
Hi, 
 Im new to visualforce and Im trying to understand why I cant iterate through the quote line items using the apex:repeat tag in the below SOQL:
 
<apex:page controller="myquote" showHeader="false" sidebar="false" >
  <apex:stylesheet value="{!URLFOR($Resource.advancedpdfresource, 'qstyles.css')}"/> 

    <apex:repeat value="{!quote}" var="qli">
        <apex:dataTable value="{!qli}" var="q" > 
            <apex:column style="border: 1px">
                <apex:facet name="header">Quote Line Item</apex:facet>
               <apex:outputText value="{!q.quotelineitems.description}"/> 

            </apex:column>
        </apex:dataTable>
    </apex:repeat>

</apex:page>

<!----Controller----->
public class myquote {

List <Quote> quote= [Select id, name, BillingAddress, BillingName, quotenumber,(SELECT id, Description, ListPrice, PricebookEntry.name, PricebookEntry.ProductCode, TotalPrice, Quantity FROM quotelineitems)  from QUOTE WHERE id=: ApexPages.CurrentPage().Getparameters().get('id')];
   
public List<Quote> getquote () {
      return quote;}          
      
                     }

 
  • October 19, 2016
  • Like
  • 0
Hi guys, the quote template has lots of limitations when it comes to styling; I would like to do something custom and need a step-by-step guide. Basically, I would like a "create quote" button on quote pagelayout that references my visualforce page and creates the pdf. 

So I think I need to follow the steps below:
1. Create a custom controller class. Can I do this with standard List controller? Basically, I have to query all the quote and quotelineitems
2. Create a new visual force page using controller from step1, add the proper styling and render as pdf.
3. Create the button on quote with a link to visual force page
Any tips and guides are appreciated. 
Thanks.

Did I miss anything? 
 
  • October 17, 2016
  • Like
  • 0
HI, 
 I have the following validation rule: 
AND( 
$Profile.Name <> "System Administrator", 
ISCHANGED(StageName), 
(TEXT(StageName) = "Closed Lost" || TEXT(StageName) = "Closed Won"))

I want only the system admin profile to able to close opportunity. Yet, I logged in as a std user and was able to create a new opportunity and set the stage to closed won and it saved the record....
  • October 13, 2016
  • Like
  • 0
Hi , 
 I already have the custom setting in my sandbox org and my test class with seealldata=true. So why am I getting this:
EXCEPTION_THROWN|[33]|System.NullPointerException: Attempt to de-reference a null object
When I test the same EXACT  test class in my developer org, I get 86% coverage. It drops to 21% in sandbox org....whats going on?
 
  • October 06, 2016
  • Like
  • 0

So I have this method that I slightly modified from http://www.valnavjo.com. 
All it does is when I press custom buttom "Generate Invoice" it uses a template "Invoice" from quotes template to generate and attach 
the invoice pdf to Attachment section of opportunity. I dont know why Im getting only 23% coverage as I invoke the method.
Also, I dont know why it is not creating the attachment in my test class when I check in system assert. Please help. 
Below is the method: 

global with sharing class InvoicePdfWsSample {
  /**
   * Default header height for invoice
   */
  private static final String DEFAULT_INVOICE_HEADER_HEIGHT = '100';
  
  /**
   * Default footer height for invoice
   */
  private static final String DEFAULT_INVOICE_FOOTER_HEIGHT = '100';
  
  /**
   * Webservice method that is called from a custom button to generate
   * an invoice PDF file using quote templates feature.
   * It generates the invoice based on:
   *     - The synced Quote, or
   *    - The latest Quote
   * If the Opportunity doesn't have any Quotes, this method doesn't do
   * anything.
   * 
   * This method uses PageReference.getContent().
   *
   * @param oppsIdList {List<Id>} list of Opportunity Ids from where the method
   *           will generate the Invoice PDF.
   * @return {String} with an error message, if any. Blank otherwise.
   */
  webService static String generateInvoicePdf(List<Id> oppsIdList) {
    try {
      //From list to set
      final Set<Id> oppsId = new Set<Id>(oppsIdList);

      //Get template Id for Invoice and url to hack pdf generation
      final String invoiceTemplateId = Application_Properties__c.getAll().get('Invoice_Template_Id').value__c;
      String invoiceHeaderHeight = Application_Properties__c.getAll().get('Invoice_Header_Height').value__c;
      String invoiceFooterHeight = Application_Properties__c.getAll().get('Invoice_Footer_Height').value__c;
      final String quoteTemplateDataViewerUrl = Application_Properties__c.getAll().get('Quote_Template_Data_Viewer_URL').value__c;
      
      //Pre-validations
      //Invoice_Template_Id and Quote_Template_Data_Viewer_URL are mandatory 
      if (String.isBlank(invoiceTemplateId) || String.isBlank(quoteTemplateDataViewerUrl)) {
        String errorMsg = 'Invoice Template Id or Quote Template Data Viewer URL are blank, please review their values in Application Properties custom setting.';

        return errorMsg;
      }
      
      //Default values for invoice header/footer height
      if (String.isBlank(invoiceHeaderHeight)) invoiceHeaderHeight = DEFAULT_INVOICE_HEADER_HEIGHT;
      if (String.isBlank(invoiceFooterHeight)) invoiceFooterHeight = DEFAULT_INVOICE_FOOTER_HEIGHT; 

      //Iterate over Opps and generate Attachments list
      final List<Attachment> attList = new List<Attachment>();
      for (Opportunity opp : [select Id,
                       (select Id, Invoice_No__c, QuotetoInvoice__c, CreatedDate
                         from Quotes 
                         order by CreatedDate DESC)
                  from Opportunity
                  where Id IN :oppsId]) {
        //No Quotes, no party
        if (opp.Quotes.isEmpty()) continue;

        //Synced quote
        Quote theQuote = null;

        //Try to get the synced one
        for (Quote quoteAux : opp.Quotes) {
          if (quoteAux.Quotetoinvoice__c) {
            theQuote = quoteAux;
           
          }
        }

        //No synced Quote, get the last one
      if (theQuote == null) return 'Select a Quote to Invoice under Quotes section';

        PageReference pageRef = new PageReference(
          quoteTemplateDataViewerUrl.replace('{!QuoteId}', theQuote.Id)
                        .replace('{!InvoiceHeaderHeight}', invoiceHeaderHeight)
                        .replace('{!InvoiceFooterHeight}', invoiceFooterHeight)
                        .replace('{!InvoiceTemplateId}', invoiceTemplateId)
        );

        attList.add(
          new Attachment(
            Name = 'Invoice #' + theQuote.Invoice_No__c + '.pdf',
            Body = pageRef.getContent(),
            ParentId = opp.Id
          )
        );
      }

      //Create Attachments
      if (!attList.isEmpty()) insert attList;
      
      return '';
    } catch (Exception e) {
      System.debug(LoggingLevel.ERROR, e.getMessage());

      final String errorMsg = 'An error has occured while generating the invoice. Details:\n\n' +
                   e.getMessage() + '\n\n' +
                   e.getStackTraceString();
      
      return errorMsg;
    }
  }
}
 

and the test class; 

 

@isTest
private class TestInvoicepdfclass {
    @isTest static void testpdfbutton(){
        
Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book 2009', Description = 'Price Book 2009 Products', IsActive = true );
    insert pb;
Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
    insert prod;

PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId(), IsActive= true); 
     insert pbe;       
           // insert opp
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity ();
         opp.name= 'Testopp';
        Opp.Accountid= acc.id;
        opp.CloseDate= date.today();
        opp.StageName= 'Closed Won';
       opp.Pricebook2id=Test.getStandardPricebookId();
        insert opp; 
            
OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
insert oppLine;       
        // insert quote
        Quote q= new Quote ();
        	 q.Name= 'Testq';
        	q.OpportunityId= Opp.id;
         	q.quotetoinvoice__C= True;
         	q.REP__C= 'AC' ;
        	q.BillingStreet= '123';
        	q.BillingCity= 'City';
        	q.BillingPostalCode= '12345';
             q.Pricebook2Id= Test.getStandardPricebookId();
           insert q;
        //add to list
         List<id> oppids= new List<id> ();
        oppids.add(opp.Id); 
        // pass the list to the method
InvoicepdfWsSample.generateInvoicepdf(oppids);
      List <Attachment> attlist= [SELECT id, name, parentid From Attachment WHERE parentid IN :oppids];
        System.assert (!attlist.isEmpty());  //check if attachment is present
        
         } 
}
 


 

  • September 28, 2016
  • Like
  • 0
isTest
private class TestInvoicepdfclass {
    static testmethod  void testpdfbutton (){
        
Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book 2009', Description = 'Price Book 2009 Products', IsActive = true );
  insert pb;
Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
  insert prod;
PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId() , IsActive= true); 
  insert pbe;      
           
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity ();
        Opportunity opp2= new Opportunity ();
        opp.name= 'Testopp';
        Opp.Accountid= acc.id;
        opp.CloseDate= date.today();
        opp.StageName= 'Closed Won';
       opp.Pricebook2id=Test.getStandardPricebookId();
        opp2.name= 'Testopp2';
        Opp2.Accountid= acc.id;
        opp2.CloseDate= date.today();
        opp2.StageName= 'Closed Won';
       opp2.Pricebook2id=Test.getStandardPricebookId();
        insert opp; insert opp2;
            
OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
insert oppLine;       
        
        Quote q= new Quote ();
        	 q.Name= 'Testq';
        	q.OpportunityId= Opp.id;
         	q.quotetoinvoice__C= True;
         	q.REP__C= 'AC' ;
        	q.BillingStreet= '123';
        	q.BillingCity= 'City';
        	q.BillingPostalCode= '12345';
             q.Pricebook2Id= Test.getStandardPricebookId();
           
        
         List<id> oppids= new List<id> ();
        oppids.add(opp.Id); oppids.add(opp2.id);
        
        
InvoicePdfWsSample.generateInvoicePdf(oppids);
       
         } 
}

I dont understand, Im using this in my test class (above) it was working fine yesterday and today, its giving me this error: 
line 9: Method does not exist or incorrect signature: Test.getStandardPricebookId()
Please advice. I'm using the same code in my other test class and it is not prompting any errors.....
  • September 28, 2016
  • Like
  • 0
Hi, 
 I found this nice workaround to use quotetemplate to make invoice pdf from this blog:  http://www.valnavjo.com/blog/how-to-create-invoices-using-quote-templates/ 
But I'm having trouble on how to write a test class for this:
global with sharing class InvoicePdfWsSample {
	/**
	 * Default header height for invoice
	 */
	private static final String DEFAULT_INVOICE_HEADER_HEIGHT = '100';
	
	/**
	 * Default footer height for invoice
	 */
	private static final String DEFAULT_INVOICE_FOOTER_HEIGHT = '100';
	
	/**
	 * Webservice method that is called from a custom button to generate
	 * an invoice PDF file using quote templates feature.
	 * It generates the invoice based on:
	 * 		- The synced Quote, or
	 *		- The latest Quote
	 * If the Opportunity doesn't have any Quotes, this method doesn't do
	 * anything.
	 * 
	 * This method uses PageReference.getContent().
	 *
	 * @param oppsIdList {List<Id>} list of Opportunity Ids from where the method
	 *					 will generate the Invoice PDF.
	 * @return {String} with an error message, if any. Blank otherwise.
	 */
	webService static String generateInvoicePdf(List<Id> oppsIdList) {
		try {
			//From list to set
			final Set<Id> oppsId = new Set<Id>(oppsIdList);

			//Get template Id for Invoice and url to hack pdf generation
			final String invoiceTemplateId = Application_Properties__c.getAll().get('Invoice_Template_Id').value__c;
			String invoiceHeaderHeight = Application_Properties__c.getAll().get('Invoice_Header_Height').value__c;
			String invoiceFooterHeight = Application_Properties__c.getAll().get('Invoice_Footer_Height').value__c;
			final String quoteTemplateDataViewerUrl = Application_Properties__c.getAll().get('Quote_Template_Data_Viewer_URL').value__c;
			
			//Pre-validations
			//Invoice_Template_Id and Quote_Template_Data_Viewer_URL are mandatory 
			if (String.isBlank(invoiceTemplateId) || String.isBlank(quoteTemplateDataViewerUrl)) {
				String errorMsg = 'Invoice Template Id or Quote Template Data Viewer URL are blank, please review their values in Application Properties custom setting.';

				return errorMsg;
			}
			
			//Default values for invoice header/footer height
			if (String.isBlank(invoiceHeaderHeight)) invoiceHeaderHeight = DEFAULT_INVOICE_HEADER_HEIGHT;
			if (String.isBlank(invoiceFooterHeight)) invoiceFooterHeight = DEFAULT_INVOICE_FOOTER_HEIGHT; 

			//Iterate over Opps and generate Attachments list
			final List<Attachment> attList = new List<Attachment>();
			for (Opportunity opp : [select Id,
										   (select Id, Invoice_No__c, IsSyncing, CreatedDate
										   	from Quotes Where QuotetoInvoice__c= True
										   	order by CreatedDate DESC)
									from Opportunity
									where Id IN :oppsId]) {
				//No Quotes, no party
				if (opp.Quotes.isEmpty()) continue;

				//Synced quote
				Quote theQuote = null;

				//Try to get the synced one
				for (Quote quoteAux : opp.Quotes) {
					if (quoteAux.IsSyncing) {
						theQuote = quoteAux;
						break;
					}
				}

				//No synced Quote, get the last one
				if (theQuote == null) theQuote = opp.Quotes.get(0);

				PageReference pageRef = new PageReference(
					quoteTemplateDataViewerUrl.replace('{!QuoteId}', theQuote.Id)
											  .replace('{!InvoiceHeaderHeight}', invoiceHeaderHeight)
											  .replace('{!InvoiceFooterHeight}', invoiceFooterHeight)
											  .replace('{!InvoiceTemplateId}', invoiceTemplateId)
				);

				attList.add(
					new Attachment(
						Name = 'Invoice #' + theQuote.Invoice_No__c + '.pdf',
						Body = pageRef.getContent(),
						ParentId = opp.Id
					)
				);
			}

			//Create Attachments
			if (!attList.isEmpty()) insert attList;
			
			return '';
		} catch (Exception e) {
			System.debug(LoggingLevel.ERROR, e.getMessage());

			final String errorMsg = 'An error has occured while generating the invoice. Details:\n\n' +
									 e.getMessage() + '\n\n' +
									 e.getStackTraceString();
			
			return errorMsg;
		}
	}
}


 
  • September 27, 2016
  • Like
  • 0
Hi , this is my first test class and Im not getting how the ids are different in this error. All Im trying to do is create a opp, quote, quotelineitem and close the opp so my trigger fires.

System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, field integrity exception: PricebookEntryId (pricebook entry is in a different pricebook than the one assigned to the opportunity): [PricebookEntryId]
@isTest
private class CreateInvoiceTestClass {
    @isTest static void insertOpp() {
               
Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book 2009', Description = 'Price Book 2009 Products', IsActive = true );
    insert pb;
Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
    insert prod;
PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId(), IsActive= true); 
     insert pbe;      
           
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity ();
        opp.name= 'Testopp';
        Opp.Accountid= acc.id;
        opp.CloseDate= date.today();
        opp.StageName= 'Qualification';
       opp.Pricebook2id=pb.id;
        
        insert opp;
        
OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
insert oppLine;       
        
        Quote q= new Quote ();
        	 q.Name= 'Testq';
        	q.OpportunityId= Opp.id;
         	q.quotetoinvoice__C= TRUE;
         	q.REP__C= 'AC' ;
        	q.BillingStreet= '123';
        	q.BillingCity= 'City';
        	q.BillingPostalCode= '12345';
             q.Pricebook2Id= pb.id;
           
        	
        	insert q;
       
      QuoteLineItem qli= new QuoteLineItem(Quoteid=q.id, PricebookEntryid= pbe.Id,  quantity=2, unitprice=10000);
        insert qli;
        opp.StageName= 'Closed Won';

									}
    
}

 
  • September 21, 2016
  • Like
  • 0
I am new to test classes, but Im pulling my hair on this one. 

Below is my trigger to create an invoice from Quote once opp is closed and quotetoinvoice checkbox is ticked: 

trigger CreateInvoice on Opportunity (after update, after insert) {
    List<Invoice__c> invoiceList = new List<Invoice__c>();
    List<InvoiceLineItem__c> invoiceLineItemList = new List<InvoiceLineItem__c>();
    Set<Id> oppIdSet = new Set<Id>();
    
    // We only care about opportunities that are closed won
    for(Opportunity o : trigger.new)
    {
        if (o.stagename=='Closed Won')
        {
            oppIdSet.add(o.id);
        }
    }

    // Need to grab all the quotes and their subsequent quotelineitems under all the opportunities that fired the trigger, but select quotes that has QuotetoInvoice=True
    List<Quote> quoteList = [SELECT id, quotenumber, Accountid, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingName, Shippingname, ShippingStreet, ShippingCity, ShippingPostalCode, ShippingState, REP__c, Totalprice, Opportunityid, name, QuotetoInvoice__c, 
                (SELECT id, ListPrice, PricebookEntry.name, PricebookEntry.ProductCode, TotalPrice, Quantity FROM quotelineitems) 
                FROM Quote WHERE Opportunityid in: oppIdSet AND QuotetoInvoice__c=TRUE]; 
    
    // Create an invoice for each quote
    for (Quote q : quoteList)
    {
        Invoice__c newInvoice = new Invoice__c();
        newInvoice.Invoice_number__c=q.quotenumber;
        newInvoice.opportunity__c= q.Opportunityid;
        newInvoice.Bill_To__c=q.Billingname;
        newInvoice.BillingStreet__c=q.Billingstreet;
        newInvoice.BillingCity__c= q.BillingCity;
        newInvoice.BillingState__c= q.BillingState;
        newInvoice.BillingZip__c= q.BillingPostalCode;
        newInvoice.Ship_To__c=q.Shippingname;
        newInvoice.ShippingStreet__c=q.Shippingstreet;
        newInvoice.ShippingCity__c= q.ShippingCity;
        newInvoice.ShippingState__c= q.ShippingState;
        newInvoice.ShippingZip__c= q.ShippingPostalCode;
        newInvoice.Total__c=q.Totalprice;
        newInvoice.Customer__c=q.Accountid;
        newInvoice.REP__c=q.REP__c;
        invoiceList.add(newInvoice);
    }
    
    // We need to insert the invoices first, before being able to insert the invoicelineitems under it.
    insert invoiceList;
    // Construct this set as a copy of the invoiceList so we can remove from it easily later
    Set<Invoice__c> invoiceSet = new Set<Invoice__c>(invoiceList);
    
    // Create the invoicelineitems
    for (Quote q : quoteList)
    {
        for (QuoteLineItem qli : q.quotelineitems)
        {
            InvoiceLineItem__c newInvLineItem = new InvoiceLineItem__c();
            newInvLineItem.Invoiceid__c=qli.id;
            newInvLineItem.Amount__c = qli.ListPrice;
            newInvLineItem.Item__c = qli.PricebookEntry.ProductCode;
            newInvLineItem.Description__c= qli.PricebookEntry.name;
            newInvLineItem.Quantity__c= qli.quantity;
            
            // We need to relate the invoicelineitem to the correct invoice
            for (Invoice__c i : invoiceSet)
            {
                // Using quote number as an identifier
                if (i.Invoice_Number__c == q.quotenumber)
                {
                    newInvLineItem.Invoiceid__c= i.id;
                }
            }
            invoiceLineItemList.add(newInvLineItem);
        }
        
        // We need to remove the invoice we just used from the set, just in case there are quotes with duplicate quote numbers.
        for (Invoice__c i : invoiceSet)
        {
            if (i.name == q.name)
            {
                invoiceSet.remove(i);
            }
        }
    }
    
    insert invoiceLineItemList;

And this is my test class: 
@isTest
private class CreateInvoiceTestClass {
    @isTest static void insertOpp() {
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity ();
        opp.name= 'Testopp';
        Opp.Accountid= acc.id;
        opp.CloseDate= date.today();
        opp.StageName= 'Qualification';
        insert opp;

Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book 2009', Description = 'Price Book 2009 Products', IsActive = true );
    insert pb;
Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
    insert prod;

Pricebook2 standardPB = [select id from Pricebook2 where isStandard=true];
PricebookEntry standardPBE = new PricebookEntry(Pricebook2Id = standardPB.Id, Product2Id = prod.Id, UnitPrice = 1000, IsActive = true);
    insert standardPBE;
PricebookEntry pbe = new PricebookEntry(Pricebook2Id = pb.Id, Product2Id = prod.Id, UnitPrice = 1000, IsActive = true);
    insert pbe;
        
       
        Quote q= new Quote ();
        	 q.Name= 'Testq';
        	q.OpportunityId= Opp.id;
         	q.quotetoinvoice__C= TRUE;
         	q.REP__C= 'AC' ;
        	q.BillingStreet= '123';
        	q.BillingCity= 'City';
        	q.BillingPostalCode= '12345';
        	q.Pricebook2Id= pb.id;
        	insert q;
       
      QuoteLineItem qli= new QuoteLineItem(id=q.Id, Product2id='Product', PricebookEntryid= pbe.Id,  quantity=2);
        insert qli;
        opp.StageName= 'Closed Won';

									}
    
}

Keep getting error:  System.QueryException: List has no rows for assignment to SObject,   Class.CreateInvoiceTestClass.insertOpp  line 18

Please help. 
 
  • September 20, 2016
  • Like
  • 0
I want to create an invoice so when my opportunity is closedwon and my custom field QuotetoInvoice  on Quote object (is checked) is true
But Im having problem with the conditinal statement.... it says "Invalid foreign key relationship: Opporutnity.quote__r "
for(Opportunity o : trigger.new)
    {
   if (o.stagename=='Closed Won'&& o.quote__r.QuotetoInvoice__c== TRUE))
        {
            oppIdSet.add(o.id);
        }
    }
Please guide me.
  • September 19, 2016
  • Like
  • 0
Hi, 
 So I created a custom object Invoice__c and Invoicelineitem__c. Basically, I want to copy all the quote line items to invoice line items. 
Below is my code: 

trigger Insertnewinvoice on Invoice__c (after insert) {
Invoice__c to=trigger.new[0];
Quote q=[Select id from Quote where id= :to.Quote__c];
list<QuoteLineItem> ql=[select id,ListPrice,PriceBookEntry.Product2Id ,Subtotal,TotalPrice from QuoteLineItem where QuoteId=:q.id];

   for(QuoteLineItem qli:ql){
       Invoicelineitem__c lm=new Invoicelineitem__c();
        lm.List_Price__c=qli.ListPrice;
         lm.Product__C=qli.PriceBookEntry.Product2Id;
         lm.Order__c=to.id;

insert lm;}}

Why am I getting error: "unexpected token 'Select id from Quote where id' ??
  • September 08, 2016
  • Like
  • 0
Hi,
 So Im trying to display a custom visualforce page to list three separate accounts (by id and name) and their related contacts list by using a custom controller below.
But I dont know how to get accounts for Acme2 and Acme3 in the class using the same method. Any help is appreciated. Thanks.
public class MyController {

    public String getName() {
        return 'MyController';
    }

    public Account getAccount() {
        return [SELECT id 
                from Account 
                 where Name = 'Acme'];                       
    } 
}

and this is my visualpage:
<apex:page controller="MyController" tabStyle="Account">
     <apex:pageBlock title="Acme"> 
        <apex:relatedList subject="{!account}" list="Contacts"/>
    </apex:pageBlock>
    <apex:pageBlock title="Acme2"> 
        <apex:relatedList subject="{!account}" list="Contacts"/>
    </apex:pageBlock>
     <apex:pageBlock title="Acme3"> 
        <apex:relatedList subject="{!account}" list="Contacts"/>
    </apex:pageBlock>
</apex:page>

 
  • August 16, 2016
  • Like
  • 0
I am creating a VF page in my developer org and getting insufficient privelages page when acessing the related list  below on account even though I am the account owner....
<apex:page standardcontroller="Account">
         <apex:detail relatedList="true">
         <apex:relatedList List="Contacts"/>
         </apex:detail>         
</apex:page>
  • August 16, 2016
  • Like
  • 0
Hi, 
 Im getting the following error and cannot figure out why: 

System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_EMAIL_ADDRESS, Email address is invalid: null: [toAddresses, null]

Class.email_class.send: line 48, column 1
Class.TestquoteEmailclass.sendEmailTestMethod: line 47, column 1

Below is my test class: 
@isTest
public class TestquoteEmailclass {
    @isTest static void sendEmailTestMethod(){
    
        Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
        insert prod;
        PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId(), IsActive= true); 
        insert pbe; 
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity (name='Testopp', Accountid=acc.id, CloseDate= date.today(), StageName='Closed Won', Pricebook2id=Test.getStandardPricebookId());
        insert opp; 
        OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
        insert oppLine;       
        Quote q= new Quote (Name='Testq', Opportunityid=opp.id, QuotetoInvoice__c= True,  Pricebook2id=Test.getStandardPricebookId());
        insert q;
    
     System.currentPagereference().getParameters().put('id',q.id);
     Attachment objAtt = new Attachment();
     objAtt.Name = 'Test';
     objAtt.body = Blob.valueof('string');
     objAtt.ParentId = q.Id;
     insert objAtt;

         ApexPages.StandardController sc =new ApexPages.StandardController(q); 
        email_class ec=new email_class (sc);
        ec.ToAddresses='test@gmail.com';
         ec.CCAddresses='test2@gmail.com';
        ec.emailCC= 'test3@gmail.com';
        ec.email_body='test1111111';
        ec.subject='test22222';
        
         Messaging.InboundEmail email = new Messaging.InboundEmail();  
         Messaging.InboundEnvelope env = new Messaging.InboundEnvelope();
        email.plainTextBody = 'This should become a note';
        email.fromAddress ='test@test.com';
        String contactEmail = 'jsmith@salesforce.com';
        email.ccAddresses = new String[] {'Jon Smith <' + contactEmail + '>'};
        email.subject = 'Dummy Account Name 123';

        ec.send();

}
}

And my controller:
public class email_class{
        
    Public string ToAddresses {get;set;}
    Public string CCAddresses {get;set;}
    Public string quoteId {get;set;}
    Public string subject {get;set;}
    public string email_body {get;set;}
    public string emailTo {get;set;}
    public string emailCC {get;set;}
     public  string [] ccaddress;   
              
        
        
        public email_class(ApexPages.StandardController controller) {
                quoteId = ApexPages.currentPage().getParameters().get('id');
    }
        
    Public PageReference send(){

                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); // set the to address
                mail.setToAddresses(new String[] {emailTo}); 
               // mail.setCCAddresses(new String[] {emailCC});   //set the cc address
                   
        if(emailCC != null && emailCC.trim() != '')
        {
        ccaddress = emailCC.split(',',0);
        mail.setCCAddresses(ccaddress);}
                                                              
   
                mail.setSubject(subject);
                mail.setBccSender(false);
                mail.setUseSignature(false);
                mail.setPlainTextBody(email_body);
               

                List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();

                for (Attachment a : [select Name, Body, BodyLength from Attachment where ParentId = :quoteId]){  // Add to attachment file list  
                        Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();  
                        efa.setFileName(a.Name); 
                        efa.setBody(a.Body); 
                        fileAttachments.add(efa);
                }
                mail.setFileAttachments(fileAttachments);// Send email
                Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
                return null;
        }
}

Any help is appreciated !
  • January 26, 2017
  • Like
  • 1
Hi, 
 I overrode the "Createpdf" button on quote page with a custom VF page (Quotecustom). I would like to create another button on quote page that lets me save that pdf as an attachment to the QuotePDF related list. 
What are the steps to do this? 
I know I need a controller that looks something like this: 
public class attachPDFToquote {
	
	private final quote q; //quote object
	
	//constructor
	public attachPDFToquote(ApexPages.StandardController standardPageController) {
		q = (Quote)standardPageController.getRecord(); //instantiate the quote object for the current record
	}
	
	//method called from the Visualforce's action attribute
	public PageReference attachPDF() {
		//generate and attach the PDF document
		PageReference pdfPage = Page.quotecustom; //create a page reference to quote custom Visualforce page
		Blob pdfBlob = pdfPage.getContent(); //get the output of the page, as displayed to a user in a browser
		Attachment attach = new Attachment(parentId = q.Id, Name = 'pdfAttachmentDemo.pdf', body = pdfBlob); //create the attachment object
		insert attach; //insert the attachment
		//redirect the user
		PageReference pageWhereWeWantToGo = new ApexPages.StandardController(q).view(); //we want to redirect the User back to the quote detail page
		pageWhereWeWantToGo.setRedirect(true); //indicate that the redirect should be performed on the client side
		return pageWhereWeWantToGo; //send the User on their way
	}

}
Im confused about the next step...do I have to create another seperate VF page or add the controller as an "extension" to my Quotecustom VF page. Please excuse my misunderstandings as Im new to this. 

THanks.
 
  • December 05, 2016
  • Like
  • 1
Hi, 
 So I have an input field :<b>To: </b> <apex:inputfield value="{!q.contactid}"/><p/>

and in my controller, I have:
public Quote q {get;set;}

and then I would like to present the email address in this outputfield: <b>NAME: </b> <apex:outputtext value="{!q.contact.name}"/><p/> 

However this is not working, Please help: 
Below is my VF page: 
<apex:page standardController="Quote" extensions="email_class">
    <apex:form >
        <apex:pageBlock title="Email Details" id="ed">
            <b>To: </b> <apex:inputfield value="{!q.contactid}"/><p/>
         <apex:actionSupport event="onchange"  action="{!Contactpopulated}" rerender="ed"/>      
               <b>NAME: </b> <apex:outputtext value="{!q.contact.email}"/><p/> 
.......
.......

and my extension:
public class email_class{
        
    Public string ToAddresses {get;set;}
    Public string CCAddresses {get;set;}
    Public string quoteId {get;set;}
    Public string subject {get;set;}
    public string email_body {get;set;}
    public string emailTo {get;set;}
    public string emailCC {get;set;}
   public Quote q {get;set;}
   // public Contact ct {get;set;}
     public  string [] ccaddress; 
       
        public email_class(ApexPages.StandardController controller) {
                quoteId = ApexPages.currentPage().getParameters().get('id');
              Quote  q= (Quote)controller.getRecord();
    }

             public void Contactpopulated(){
       q.contact=[ Select  email, name From contact where  id=:q.contactid limit 1]; 
}


 
  • March 09, 2017
  • Like
  • 0
I can excute this fine in excute anonymous, but when I put it in the class, I get "Unexpected token: '='" at LINE 5
 
public class testProduct {
    
    public Product2 shipping {get;set;} 
    
    shipping = [SELECT id, name, productcode from PRODUCT2 where productcode='shipping' limit 1];

 
  • February 27, 2017
  • Like
  • 0
I dont get this error...I tried retyping the quotes. Please help:


System.QueryException: line 1:282 no viable alternative at character '%'
Class.opportunityProductEntryExtension.updateAvailableList: line 119, column 1
Class.opportunityProductEntryTests.theTests: line 53, column 1


 
public with sharing class opportunityProductEntryExtension {

    public Opportunity theOpp {get;set;}
    public String searchString {get;set;}
    public opportunityLineItem[] shoppingCart {get;set;}
    public priceBookEntry[] AvailableProducts {get;set;}
    public Pricebook2 theBook {get;set;}   
    
    public String toSelect {get; set;}
    public String toUnselect {get; set;}
    public Decimal Total {get;set;}
    
    public Boolean overLimit {get;set;}
    public Boolean multipleCurrencies {get; set;}
    
    private Boolean forcePricebookSelection = false;
    
    private opportunityLineItem[] forDeletion = new opportunityLineItem[]{};


    public opportunityProductEntryExtension(ApexPages.StandardController controller) {

        // Need to know if org has multiple currencies enabled
        multipleCurrencies = UserInfo.isMultiCurrencyOrganization();

        // Get information about the Opportunity being worked on
        if(multipleCurrencies)
            theOpp = database.query('select Id, Pricebook2Id, Pricebook2.Name, CurrencyIsoCode from Opportunity where Id = \'' + controller.getRecord().Id + '\' limit 1');
        else
            theOpp = [select Id, Pricebook2Id, PriceBook2.Name from Opportunity where Id = :controller.getRecord().Id limit 1];
        
        // If products were previously selected need to put them in the "selected products" section to start with
        shoppingCart = [select Id, Quantity, TotalPrice, UnitPrice, Description, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.PriceBook2Id from opportunityLineItem where OpportunityId=:theOpp.Id];

        // Check if Opp has a pricebook associated yet
        if(theOpp.Pricebook2Id == null){
            Pricebook2[] activepbs = [select Id, Name from Pricebook2 where isActive = true limit 2];
            if(activepbs.size() == 2){
                forcePricebookSelection = true;
                theBook = new Pricebook2();
            }
            else{
                theBook = activepbs[0];
            }
        }
        else{
            theBook = theOpp.Pricebook2;
        }
        
        if(!forcePricebookSelection)
            updateAvailableList();
    }
    
    // this is the 'action' method on the page
    public PageReference priceBookCheck(){
    
        // if the user needs to select a pricebook before we proceed we send them to standard pricebook selection screen
        if(forcePricebookSelection){        
            return changePricebook();
        }
        else{
        
            //if there is only one active pricebook we go with it and save the opp
            if(theOpp.pricebook2Id != theBook.Id){
                try{
                    theOpp.Pricebook2Id = theBook.Id;
                    update(theOpp);
                }
                catch(Exception e){
                    ApexPages.addMessages(e);
                }
            }
            
            return null;
        }
    }
       
    public String getChosenCurrency(){
    
        if(multipleCurrencies)
            return (String)theOpp.get('CurrencyIsoCode');
        else
            return '';
    }

    public void updateAvailableList() {
    
        // We dynamically build a query string and exclude items already in the shopping cart
        String qString = 'select Id, Pricebook2Id, IsActive, Product2.Name, Product2.ProductCode, Product2.Family, Product2.IsActive, Product2.Description, UnitPrice from PricebookEntry where IsActive=true and Pricebook2Id = \'' + theBook.Id + '\'';
        if(multipleCurrencies)
            qstring += 'and CurrencyIsoCode = \'' + theOpp.get('currencyIsoCode') + '\'';
        
        // note that we are looking for the search string entered by the user in the name OR description
        // modify this to search other fields if desired
        if(searchString!=null){
            qString+= 'and (Product2.Name like \'%' + searchString + '%\' or Product2.ProductCode like \'%' + searchString + '%\')';
        }
        
        Set<Id> selectedEntries = new Set<Id>();
        for(opportunityLineItem d:shoppingCart){
            selectedEntries.add(d.PricebookEntryId);
        }
        
        if(selectedEntries.size()>0){
            String tempFilter = ' and Id not in (';
            for(Id i : selectedEntries){
                tempFilter+= '\'' + (String)i + '\',';
            }
            String extraFilter = tempFilter.substring(0,tempFilter.length()-1);
            extraFilter+= ')';
            
            qString+= extraFilter;
        }
        
        qString+= ' order by Product2.Name';
        qString+= ' limit 101';
        
        system.debug('qString:' +qString);        
        AvailableProducts = database.query(qString);
        
        // We only display up to 100 results... if there are more than we let the user know (see vf page)
        if(AvailableProducts.size()==101){
            AvailableProducts.remove(100);
            overLimit = true;
        }
        else{
            overLimit=false;
        }
    }
    
    public void addToShoppingCart(){
    
        // This function runs when a user hits "select" button next to a product
    
        for(PricebookEntry d : AvailableProducts){
            if((String)d.Id==toSelect){
                shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, UnitPrice=d.UnitPrice));
                break;
            }
        }
        
        updateAvailableList();  
    }
    

    public PageReference removeFromShoppingCart(){
    
        // This function runs when a user hits "remove" on an item in the "Selected Products" section
    
        Integer count = 0;
    
        for(opportunityLineItem d : shoppingCart){
            if((String)d.PriceBookEntryId==toUnselect){
            
                if(d.Id!=null)
                    forDeletion.add(d);
            
                shoppingCart.remove(count);
                break;
            }
            count++;
        }
        
        updateAvailableList();
        
        return null;
    }
    
    public PageReference onSave(){
    
        // If previously selected products are now removed, we need to delete them
        if(forDeletion.size()>0)
            delete(forDeletion);
    
        // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here
        try{
            if(shoppingCart.size()>0)
                upsert(shoppingCart);
        }
        catch(Exception e){
            ApexPages.addMessages(e);
            return null;
        }  
           
        // After save return the user to the Opportunity
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }
    
    public PageReference onCancel(){
 
        // If user hits cancel we commit no changes and return them to the Opportunity   
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }
    
    public PageReference changePricebook(){
    
        // This simply returns a PageReference to the standard Pricebook selection screen
        // Note that is uses retURL parameter to make sure the user is sent back after they choose
    
        PageReference ref = new PageReference('/oppitm/choosepricebook.jsp');
        ref.getParameters().put('id',theOpp.Id);
        ref.getParameters().put('retURL','/apex/opportunityProductEntry?id=' + theOpp.Id);
        
        return ref;
    }
}


 
@istest
private class opportunityProductEntryTests {

    static testMethod void theTests(){
        
        // You really should create test data, but I'm going to query instead
        // It's my best shot of avoiding a test failure in most orgs
        // Once you've installed this package though, you might want to write your own tests
        // or at least customize these ones to make them more applicable to your org
            
        OpportunityLineItem oli = [select Id, PricebookEntryId, PricebookEntry.Pricebook2Id, PricebookEntry.Name, PriceBookEntry.Product2Id, OpportunityId, Opportunity.AccountId from OpportunityLineItem limit 1];
               
                
        ////////////////////////////////////////
        //  test opportunityProductEntry
        ////////////////////////////////////////
        
        // load the page       
        PageReference pageRef = Page.opportunityProductEntry;
        pageRef.getParameters().put('Id',oli.OpportunityId);
        Test.setCurrentPageReference(pageRef);
        
        // load the extension
        opportunityProductEntryExtension oPEE = new opportunityProductEntryExtension(new ApexPages.StandardController(oli.Opportunity));
        
        // test 'getChosenCurrency' method
        if(UserInfo.isMultiCurrencyOrganization())
            System.assert(oPEE.getChosenCurrency()!='');
        else
            System.assertEquals(oPEE.getChosenCurrency(),'');

        // we know that there is at least one line item, so we confirm
        Integer startCount = oPEE.ShoppingCart.size();
        system.assert(startCount>0);

        //test search functionality without finding anything
        oPEE.searchString = 'michaelforce is a hip cat';
        oPEE.updateAvailableList();
        system.assert(oPEE.AvailableProducts.size()==0);
        
        //test remove from shopping cart
        oPEE.toUnselect = oli.PricebookEntryId;
        oPEE.removeFromShoppingCart();
        system.assert(oPEE.shoppingCart.size()==startCount-1);
        
        //test save and reload extension
        oPEE.onSave();
        oPEE = new opportunityProductEntryExtension(new ApexPages.StandardController(oli.Opportunity));
        system.assert(oPEE.shoppingCart.size()==startCount-1);
        
        // test search again, this time we will find something
        oPEE.searchString = oli.PricebookEntry.Name;
        oPEE.updateAvailableList();
        system.assert(oPEE.AvailableProducts.size()>0);       

        // test add to Shopping Cart function
        oPEE.toSelect = oPEE.AvailableProducts[0].Id;
        oPEE.addToShoppingCart();
        system.assert(oPEE.shoppingCart.size()==startCount);
                
        // test save method - WITHOUT quanitities and amounts entered and confirm that error message is displayed
        oPEE.onSave();
        system.assert(ApexPages.getMessages().size()>0);
       
        opEE.addtoshoppingcart();
        
        // add required info and try save again
        for(OpportunityLineItem o : oPEE.ShoppingCart){
            o.quantity = 5;
            o.unitprice = 300;
        }
        oPEE.onSave();
        
        // query line items to confirm that the save worked
        opportunityLineItem[] oli2 = [select Id from opportunityLineItem where OpportunityId = :oli.OpportunityId];
        system.assert(oli2.size()==startCount);
        
        // test on new Opp (no pricebook selected) to make sure redirect is happening
        Opportunity newOpp = new Opportunity(Name='New Opp',stageName='Pipeline',Amount=10,closeDate=System.Today()+30,AccountId=oli.Opportunity.AccountId);
        insert(newOpp);
        oPEE = new opportunityProductEntryExtension(new ApexPages.StandardController(newOpp));
        System.assert(oPEE.priceBookCheck()!=null);
        
        // final quick check of cancel button
        System.assert(oPEE.onCancel()!=null);
        
        
        ////////////////////////////////////////
        //  test redirect page
        ////////////////////////////////////////
        
        // load the page
        pageRef = Page.opportunityProductRedirect;
        pageRef.getParameters().put('Id',oli2[0].Id);
        Test.setCurrentPageReference(pageRef);

        // load the extension and confirm that redirect function returns something
        opportunityProductRedirectExtension oPRE = new opportunityProductRedirectExtension(new ApexPages.StandardController(oli2[0]));
        System.assert(oPRE.redirect()!=null);
     
    }
}

 
  • February 07, 2017
  • Like
  • 0
Hi, 
 Im getting the following error and cannot figure out why: 

System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_EMAIL_ADDRESS, Email address is invalid: null: [toAddresses, null]

Class.email_class.send: line 48, column 1
Class.TestquoteEmailclass.sendEmailTestMethod: line 47, column 1

Below is my test class: 
@isTest
public class TestquoteEmailclass {
    @isTest static void sendEmailTestMethod(){
    
        Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
        insert prod;
        PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId(), IsActive= true); 
        insert pbe; 
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity (name='Testopp', Accountid=acc.id, CloseDate= date.today(), StageName='Closed Won', Pricebook2id=Test.getStandardPricebookId());
        insert opp; 
        OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
        insert oppLine;       
        Quote q= new Quote (Name='Testq', Opportunityid=opp.id, QuotetoInvoice__c= True,  Pricebook2id=Test.getStandardPricebookId());
        insert q;
    
     System.currentPagereference().getParameters().put('id',q.id);
     Attachment objAtt = new Attachment();
     objAtt.Name = 'Test';
     objAtt.body = Blob.valueof('string');
     objAtt.ParentId = q.Id;
     insert objAtt;

         ApexPages.StandardController sc =new ApexPages.StandardController(q); 
        email_class ec=new email_class (sc);
        ec.ToAddresses='test@gmail.com';
         ec.CCAddresses='test2@gmail.com';
        ec.emailCC= 'test3@gmail.com';
        ec.email_body='test1111111';
        ec.subject='test22222';
        
         Messaging.InboundEmail email = new Messaging.InboundEmail();  
         Messaging.InboundEnvelope env = new Messaging.InboundEnvelope();
        email.plainTextBody = 'This should become a note';
        email.fromAddress ='test@test.com';
        String contactEmail = 'jsmith@salesforce.com';
        email.ccAddresses = new String[] {'Jon Smith <' + contactEmail + '>'};
        email.subject = 'Dummy Account Name 123';

        ec.send();

}
}

And my controller:
public class email_class{
        
    Public string ToAddresses {get;set;}
    Public string CCAddresses {get;set;}
    Public string quoteId {get;set;}
    Public string subject {get;set;}
    public string email_body {get;set;}
    public string emailTo {get;set;}
    public string emailCC {get;set;}
     public  string [] ccaddress;   
              
        
        
        public email_class(ApexPages.StandardController controller) {
                quoteId = ApexPages.currentPage().getParameters().get('id');
    }
        
    Public PageReference send(){

                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); // set the to address
                mail.setToAddresses(new String[] {emailTo}); 
               // mail.setCCAddresses(new String[] {emailCC});   //set the cc address
                   
        if(emailCC != null && emailCC.trim() != '')
        {
        ccaddress = emailCC.split(',',0);
        mail.setCCAddresses(ccaddress);}
                                                              
   
                mail.setSubject(subject);
                mail.setBccSender(false);
                mail.setUseSignature(false);
                mail.setPlainTextBody(email_body);
               

                List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();

                for (Attachment a : [select Name, Body, BodyLength from Attachment where ParentId = :quoteId]){  // Add to attachment file list  
                        Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();  
                        efa.setFileName(a.Name); 
                        efa.setBody(a.Body); 
                        fileAttachments.add(efa);
                }
                mail.setFileAttachments(fileAttachments);// Send email
                Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
                return null;
        }
}

Any help is appreciated !
  • January 26, 2017
  • Like
  • 1
Hi, 
 I overrode the "Createpdf" button on quote page with a custom VF page (Quotecustom). I would like to create another button on quote page that lets me save that pdf as an attachment to the QuotePDF related list. 
What are the steps to do this? 
I know I need a controller that looks something like this: 
public class attachPDFToquote {
	
	private final quote q; //quote object
	
	//constructor
	public attachPDFToquote(ApexPages.StandardController standardPageController) {
		q = (Quote)standardPageController.getRecord(); //instantiate the quote object for the current record
	}
	
	//method called from the Visualforce's action attribute
	public PageReference attachPDF() {
		//generate and attach the PDF document
		PageReference pdfPage = Page.quotecustom; //create a page reference to quote custom Visualforce page
		Blob pdfBlob = pdfPage.getContent(); //get the output of the page, as displayed to a user in a browser
		Attachment attach = new Attachment(parentId = q.Id, Name = 'pdfAttachmentDemo.pdf', body = pdfBlob); //create the attachment object
		insert attach; //insert the attachment
		//redirect the user
		PageReference pageWhereWeWantToGo = new ApexPages.StandardController(q).view(); //we want to redirect the User back to the quote detail page
		pageWhereWeWantToGo.setRedirect(true); //indicate that the redirect should be performed on the client side
		return pageWhereWeWantToGo; //send the User on their way
	}

}
Im confused about the next step...do I have to create another seperate VF page or add the controller as an "extension" to my Quotecustom VF page. Please excuse my misunderstandings as Im new to this. 

THanks.
 
  • December 05, 2016
  • Like
  • 1
So I have custom object (invoice__c) with master-detail to opportunity. I have a custom field (AF_LastSyncTime__c ) on invoice__c that Im trying to access.
so here is my query:
for (Opportunity opp : [select Id, (select id, AF_LastSyncTime__c from Invoices__r),
                       (select Id, Invoice_No__c, QuotetoInvoice__c, CreatedDate
                         from Quotes 
                         order by CreatedDate DESC)
                  from Opportunity
                  where Id IN :oppsId]) 
{

 if (opp.Invoices__r.AF_LastSyncTime__c==null) return 'blah blah blah'; 

 }

No error with query, but I get invalid foreign key relationship in the if statement....why is that?

Thanks.
  • November 01, 2016
  • Like
  • 0
Hi, 
 Im new to visualforce and Im trying to understand why I cant iterate through the quote line items using the apex:repeat tag in the below SOQL:
 
<apex:page controller="myquote" showHeader="false" sidebar="false" >
  <apex:stylesheet value="{!URLFOR($Resource.advancedpdfresource, 'qstyles.css')}"/> 

    <apex:repeat value="{!quote}" var="qli">
        <apex:dataTable value="{!qli}" var="q" > 
            <apex:column style="border: 1px">
                <apex:facet name="header">Quote Line Item</apex:facet>
               <apex:outputText value="{!q.quotelineitems.description}"/> 

            </apex:column>
        </apex:dataTable>
    </apex:repeat>

</apex:page>

<!----Controller----->
public class myquote {

List <Quote> quote= [Select id, name, BillingAddress, BillingName, quotenumber,(SELECT id, Description, ListPrice, PricebookEntry.name, PricebookEntry.ProductCode, TotalPrice, Quantity FROM quotelineitems)  from QUOTE WHERE id=: ApexPages.CurrentPage().Getparameters().get('id')];
   
public List<Quote> getquote () {
      return quote;}          
      
                     }

 
  • October 19, 2016
  • Like
  • 0
Hi guys, the quote template has lots of limitations when it comes to styling; I would like to do something custom and need a step-by-step guide. Basically, I would like a "create quote" button on quote pagelayout that references my visualforce page and creates the pdf. 

So I think I need to follow the steps below:
1. Create a custom controller class. Can I do this with standard List controller? Basically, I have to query all the quote and quotelineitems
2. Create a new visual force page using controller from step1, add the proper styling and render as pdf.
3. Create the button on quote with a link to visual force page
Any tips and guides are appreciated. 
Thanks.

Did I miss anything? 
 
  • October 17, 2016
  • Like
  • 0
HI, 
 I have the following validation rule: 
AND( 
$Profile.Name <> "System Administrator", 
ISCHANGED(StageName), 
(TEXT(StageName) = "Closed Lost" || TEXT(StageName) = "Closed Won"))

I want only the system admin profile to able to close opportunity. Yet, I logged in as a std user and was able to create a new opportunity and set the stage to closed won and it saved the record....
  • October 13, 2016
  • Like
  • 0
Hi , 
 I already have the custom setting in my sandbox org and my test class with seealldata=true. So why am I getting this:
EXCEPTION_THROWN|[33]|System.NullPointerException: Attempt to de-reference a null object
When I test the same EXACT  test class in my developer org, I get 86% coverage. It drops to 21% in sandbox org....whats going on?
 
  • October 06, 2016
  • Like
  • 0
isTest
private class TestInvoicepdfclass {
    static testmethod  void testpdfbutton (){
        
Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book 2009', Description = 'Price Book 2009 Products', IsActive = true );
  insert pb;
Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
  insert prod;
PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId() , IsActive= true); 
  insert pbe;      
           
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity ();
        Opportunity opp2= new Opportunity ();
        opp.name= 'Testopp';
        Opp.Accountid= acc.id;
        opp.CloseDate= date.today();
        opp.StageName= 'Closed Won';
       opp.Pricebook2id=Test.getStandardPricebookId();
        opp2.name= 'Testopp2';
        Opp2.Accountid= acc.id;
        opp2.CloseDate= date.today();
        opp2.StageName= 'Closed Won';
       opp2.Pricebook2id=Test.getStandardPricebookId();
        insert opp; insert opp2;
            
OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
insert oppLine;       
        
        Quote q= new Quote ();
        	 q.Name= 'Testq';
        	q.OpportunityId= Opp.id;
         	q.quotetoinvoice__C= True;
         	q.REP__C= 'AC' ;
        	q.BillingStreet= '123';
        	q.BillingCity= 'City';
        	q.BillingPostalCode= '12345';
             q.Pricebook2Id= Test.getStandardPricebookId();
           
        
         List<id> oppids= new List<id> ();
        oppids.add(opp.Id); oppids.add(opp2.id);
        
        
InvoicePdfWsSample.generateInvoicePdf(oppids);
       
         } 
}

I dont understand, Im using this in my test class (above) it was working fine yesterday and today, its giving me this error: 
line 9: Method does not exist or incorrect signature: Test.getStandardPricebookId()
Please advice. I'm using the same code in my other test class and it is not prompting any errors.....
  • September 28, 2016
  • Like
  • 0
Hi, 
 I found this nice workaround to use quotetemplate to make invoice pdf from this blog:  http://www.valnavjo.com/blog/how-to-create-invoices-using-quote-templates/ 
But I'm having trouble on how to write a test class for this:
global with sharing class InvoicePdfWsSample {
	/**
	 * Default header height for invoice
	 */
	private static final String DEFAULT_INVOICE_HEADER_HEIGHT = '100';
	
	/**
	 * Default footer height for invoice
	 */
	private static final String DEFAULT_INVOICE_FOOTER_HEIGHT = '100';
	
	/**
	 * Webservice method that is called from a custom button to generate
	 * an invoice PDF file using quote templates feature.
	 * It generates the invoice based on:
	 * 		- The synced Quote, or
	 *		- The latest Quote
	 * If the Opportunity doesn't have any Quotes, this method doesn't do
	 * anything.
	 * 
	 * This method uses PageReference.getContent().
	 *
	 * @param oppsIdList {List<Id>} list of Opportunity Ids from where the method
	 *					 will generate the Invoice PDF.
	 * @return {String} with an error message, if any. Blank otherwise.
	 */
	webService static String generateInvoicePdf(List<Id> oppsIdList) {
		try {
			//From list to set
			final Set<Id> oppsId = new Set<Id>(oppsIdList);

			//Get template Id for Invoice and url to hack pdf generation
			final String invoiceTemplateId = Application_Properties__c.getAll().get('Invoice_Template_Id').value__c;
			String invoiceHeaderHeight = Application_Properties__c.getAll().get('Invoice_Header_Height').value__c;
			String invoiceFooterHeight = Application_Properties__c.getAll().get('Invoice_Footer_Height').value__c;
			final String quoteTemplateDataViewerUrl = Application_Properties__c.getAll().get('Quote_Template_Data_Viewer_URL').value__c;
			
			//Pre-validations
			//Invoice_Template_Id and Quote_Template_Data_Viewer_URL are mandatory 
			if (String.isBlank(invoiceTemplateId) || String.isBlank(quoteTemplateDataViewerUrl)) {
				String errorMsg = 'Invoice Template Id or Quote Template Data Viewer URL are blank, please review their values in Application Properties custom setting.';

				return errorMsg;
			}
			
			//Default values for invoice header/footer height
			if (String.isBlank(invoiceHeaderHeight)) invoiceHeaderHeight = DEFAULT_INVOICE_HEADER_HEIGHT;
			if (String.isBlank(invoiceFooterHeight)) invoiceFooterHeight = DEFAULT_INVOICE_FOOTER_HEIGHT; 

			//Iterate over Opps and generate Attachments list
			final List<Attachment> attList = new List<Attachment>();
			for (Opportunity opp : [select Id,
										   (select Id, Invoice_No__c, IsSyncing, CreatedDate
										   	from Quotes Where QuotetoInvoice__c= True
										   	order by CreatedDate DESC)
									from Opportunity
									where Id IN :oppsId]) {
				//No Quotes, no party
				if (opp.Quotes.isEmpty()) continue;

				//Synced quote
				Quote theQuote = null;

				//Try to get the synced one
				for (Quote quoteAux : opp.Quotes) {
					if (quoteAux.IsSyncing) {
						theQuote = quoteAux;
						break;
					}
				}

				//No synced Quote, get the last one
				if (theQuote == null) theQuote = opp.Quotes.get(0);

				PageReference pageRef = new PageReference(
					quoteTemplateDataViewerUrl.replace('{!QuoteId}', theQuote.Id)
											  .replace('{!InvoiceHeaderHeight}', invoiceHeaderHeight)
											  .replace('{!InvoiceFooterHeight}', invoiceFooterHeight)
											  .replace('{!InvoiceTemplateId}', invoiceTemplateId)
				);

				attList.add(
					new Attachment(
						Name = 'Invoice #' + theQuote.Invoice_No__c + '.pdf',
						Body = pageRef.getContent(),
						ParentId = opp.Id
					)
				);
			}

			//Create Attachments
			if (!attList.isEmpty()) insert attList;
			
			return '';
		} catch (Exception e) {
			System.debug(LoggingLevel.ERROR, e.getMessage());

			final String errorMsg = 'An error has occured while generating the invoice. Details:\n\n' +
									 e.getMessage() + '\n\n' +
									 e.getStackTraceString();
			
			return errorMsg;
		}
	}
}


 
  • September 27, 2016
  • Like
  • 0
Hi , this is my first test class and Im not getting how the ids are different in this error. All Im trying to do is create a opp, quote, quotelineitem and close the opp so my trigger fires.

System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, field integrity exception: PricebookEntryId (pricebook entry is in a different pricebook than the one assigned to the opportunity): [PricebookEntryId]
@isTest
private class CreateInvoiceTestClass {
    @isTest static void insertOpp() {
               
Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book 2009', Description = 'Price Book 2009 Products', IsActive = true );
    insert pb;
Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
    insert prod;
PricebookEntry pbe=new PricebookEntry(unitprice=0.01,Product2Id=prod.Id, Pricebook2Id=Test.getStandardPricebookId(), IsActive= true); 
     insert pbe;      
           
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity ();
        opp.name= 'Testopp';
        Opp.Accountid= acc.id;
        opp.CloseDate= date.today();
        opp.StageName= 'Qualification';
       opp.Pricebook2id=pb.id;
        
        insert opp;
        
OpportunityLineItem oppLine = new OpportunityLineItem( pricebookentryid=pbe.Id,TotalPrice=2000, Quantity = 2,Opportunityid = opp.Id);
insert oppLine;       
        
        Quote q= new Quote ();
        	 q.Name= 'Testq';
        	q.OpportunityId= Opp.id;
         	q.quotetoinvoice__C= TRUE;
         	q.REP__C= 'AC' ;
        	q.BillingStreet= '123';
        	q.BillingCity= 'City';
        	q.BillingPostalCode= '12345';
             q.Pricebook2Id= pb.id;
           
        	
        	insert q;
       
      QuoteLineItem qli= new QuoteLineItem(Quoteid=q.id, PricebookEntryid= pbe.Id,  quantity=2, unitprice=10000);
        insert qli;
        opp.StageName= 'Closed Won';

									}
    
}

 
  • September 21, 2016
  • Like
  • 0
I am new to test classes, but Im pulling my hair on this one. 

Below is my trigger to create an invoice from Quote once opp is closed and quotetoinvoice checkbox is ticked: 

trigger CreateInvoice on Opportunity (after update, after insert) {
    List<Invoice__c> invoiceList = new List<Invoice__c>();
    List<InvoiceLineItem__c> invoiceLineItemList = new List<InvoiceLineItem__c>();
    Set<Id> oppIdSet = new Set<Id>();
    
    // We only care about opportunities that are closed won
    for(Opportunity o : trigger.new)
    {
        if (o.stagename=='Closed Won')
        {
            oppIdSet.add(o.id);
        }
    }

    // Need to grab all the quotes and their subsequent quotelineitems under all the opportunities that fired the trigger, but select quotes that has QuotetoInvoice=True
    List<Quote> quoteList = [SELECT id, quotenumber, Accountid, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingName, Shippingname, ShippingStreet, ShippingCity, ShippingPostalCode, ShippingState, REP__c, Totalprice, Opportunityid, name, QuotetoInvoice__c, 
                (SELECT id, ListPrice, PricebookEntry.name, PricebookEntry.ProductCode, TotalPrice, Quantity FROM quotelineitems) 
                FROM Quote WHERE Opportunityid in: oppIdSet AND QuotetoInvoice__c=TRUE]; 
    
    // Create an invoice for each quote
    for (Quote q : quoteList)
    {
        Invoice__c newInvoice = new Invoice__c();
        newInvoice.Invoice_number__c=q.quotenumber;
        newInvoice.opportunity__c= q.Opportunityid;
        newInvoice.Bill_To__c=q.Billingname;
        newInvoice.BillingStreet__c=q.Billingstreet;
        newInvoice.BillingCity__c= q.BillingCity;
        newInvoice.BillingState__c= q.BillingState;
        newInvoice.BillingZip__c= q.BillingPostalCode;
        newInvoice.Ship_To__c=q.Shippingname;
        newInvoice.ShippingStreet__c=q.Shippingstreet;
        newInvoice.ShippingCity__c= q.ShippingCity;
        newInvoice.ShippingState__c= q.ShippingState;
        newInvoice.ShippingZip__c= q.ShippingPostalCode;
        newInvoice.Total__c=q.Totalprice;
        newInvoice.Customer__c=q.Accountid;
        newInvoice.REP__c=q.REP__c;
        invoiceList.add(newInvoice);
    }
    
    // We need to insert the invoices first, before being able to insert the invoicelineitems under it.
    insert invoiceList;
    // Construct this set as a copy of the invoiceList so we can remove from it easily later
    Set<Invoice__c> invoiceSet = new Set<Invoice__c>(invoiceList);
    
    // Create the invoicelineitems
    for (Quote q : quoteList)
    {
        for (QuoteLineItem qli : q.quotelineitems)
        {
            InvoiceLineItem__c newInvLineItem = new InvoiceLineItem__c();
            newInvLineItem.Invoiceid__c=qli.id;
            newInvLineItem.Amount__c = qli.ListPrice;
            newInvLineItem.Item__c = qli.PricebookEntry.ProductCode;
            newInvLineItem.Description__c= qli.PricebookEntry.name;
            newInvLineItem.Quantity__c= qli.quantity;
            
            // We need to relate the invoicelineitem to the correct invoice
            for (Invoice__c i : invoiceSet)
            {
                // Using quote number as an identifier
                if (i.Invoice_Number__c == q.quotenumber)
                {
                    newInvLineItem.Invoiceid__c= i.id;
                }
            }
            invoiceLineItemList.add(newInvLineItem);
        }
        
        // We need to remove the invoice we just used from the set, just in case there are quotes with duplicate quote numbers.
        for (Invoice__c i : invoiceSet)
        {
            if (i.name == q.name)
            {
                invoiceSet.remove(i);
            }
        }
    }
    
    insert invoiceLineItemList;

And this is my test class: 
@isTest
private class CreateInvoiceTestClass {
    @isTest static void insertOpp() {
        Account acc = new Account (name='Acme');
        insert acc;
        Opportunity opp= new Opportunity ();
        opp.name= 'Testopp';
        Opp.Accountid= acc.id;
        opp.CloseDate= date.today();
        opp.StageName= 'Qualification';
        insert opp;

Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book 2009', Description = 'Price Book 2009 Products', IsActive = true );
    insert pb;
Product2 prod = new Product2(Name = 'SLA: Bronze', IsActive = true);
    insert prod;

Pricebook2 standardPB = [select id from Pricebook2 where isStandard=true];
PricebookEntry standardPBE = new PricebookEntry(Pricebook2Id = standardPB.Id, Product2Id = prod.Id, UnitPrice = 1000, IsActive = true);
    insert standardPBE;
PricebookEntry pbe = new PricebookEntry(Pricebook2Id = pb.Id, Product2Id = prod.Id, UnitPrice = 1000, IsActive = true);
    insert pbe;
        
       
        Quote q= new Quote ();
        	 q.Name= 'Testq';
        	q.OpportunityId= Opp.id;
         	q.quotetoinvoice__C= TRUE;
         	q.REP__C= 'AC' ;
        	q.BillingStreet= '123';
        	q.BillingCity= 'City';
        	q.BillingPostalCode= '12345';
        	q.Pricebook2Id= pb.id;
        	insert q;
       
      QuoteLineItem qli= new QuoteLineItem(id=q.Id, Product2id='Product', PricebookEntryid= pbe.Id,  quantity=2);
        insert qli;
        opp.StageName= 'Closed Won';

									}
    
}

Keep getting error:  System.QueryException: List has no rows for assignment to SObject,   Class.CreateInvoiceTestClass.insertOpp  line 18

Please help. 
 
  • September 20, 2016
  • Like
  • 0