Tag Archives: Apex

[repost ]Search Records In Salesforce Using Auto Complete Feature

original:http://sfdchack.blogspot.com/2013/04/search-records-in-salesforce-using-auto.html

To retrieve a particular record is a basic need of any kind of the business requirement in Salesforce, Salesforce has provided us an out of box functionality known as global search or we can say standard search functionality, What theGlobal Search does? Global Search Searches all record having the same name in all objects but sometime client requires some custom search which I have Implemented the same feature, Just customize as per client requirement the example given below It searches record in few object like Account, Case, Contact  Opportunity only this is a limited search as per the client need as I have discussed above, For example client does not want to search the Custom object having the same name of the Contact object name and wants auto suggest feature as well then we can implement this feature, Some client prefers tab feature to get record of different -2 sObject into the single page. Some client requires some more advance feature like Google search having the same feature of Google search like auto suggest feature. So here I have tried to developed same functionality, The auto complete feature is the most important and the complex logic as we all know, to achieve this functionality I have just written a piece of code in jQuery,JavaScript Remoting using Remoting notation in Apex and obviously Visualforce page having few static resource. Here is Video Demo link and the running Demo which you can run at your end.

Implementation of this functionality in your org you do not need to put extra effort you just need to copy and paste all component given above in a sequence like class should be created first before creating Visualforce page, when you creating Visualforce page at that time you need static resource please Download from here. So the sequence isClass > Static Resource > Visualforce page now you are ready to test at your end!

Apex Controller

/*
    @Author: Mohammad Usman
    @Version: 1.0
    @Description: Auto complete and search funtionalty for the Account,Lead,Opportunity and Contact objects.
*/
global class AutoCompleteController {
    /*Global variables*/
    public String searchedRecordId { get; set; }
    public static list<ResultSet> searchedRecord {get;set;}    
    @RemoteAction
    global static ResultSet[] getRecords(String searchText) {
        //sObject List
        searchedRecord = new list<ResultSet>();
        //SOSL Text should be more then one charecter
        if(searchText.length() >0){
            //SOSL opretion to retrive records of the Account, Lead, Contact, Opportunity, Lead Objects you can add more.
            List<List<SObject>> searchList = [FIND :searchText IN ALL FIELDS RETURNING Account (Id, Name,Description), Contact(Id, Name,Description), Opportunity(Id, Name,Description), Lead(Id, Name,Description)];
            //Adding diffrent object's records in sobject list
            for(List<SObject> o:searchList){
                for(SObject s:o){
                    searchedRecord.add(new autoCompleteController.ResultSet(s));
                } 
            }
        }
        return searchedRecord;
    }
    /*getGlobalDescribed*/
    private static list<Schema.SObjectType> gd{
        get{
            if(gd == null){
                gd = Schema.getGlobalDescribe().values();
            }
            return gd ;
        }set;
    }
    /*Record Wrapper*/
    global class ResultSet{
        public String Id {get;set;} 
        public String Name{get;set;}
        public String Description {get;set;}
        public String sObjectName {get;set;}
        public ResultSet(sObject s){
            this.Id = s.Id;
            this.Name = s.get('Name')+'';
            this.Description = s.get('Description')+'';
            this.sObjectName = getsObjectNameById(Id);            
        }       
        global ResultSet(String Id,String Name,String Description){
            this.Id = Id;
            this.Name = Name;
            this.Description = Description;
            this.sObjectName = getsObjectNameById(Id);            
        }
        /*To get object Name by Id*/
        private String getsObjectNameById(String sObjectId){            
            if(sObjectId != null && sObjectId.trim() != ''){
                for(Schema.SObjectType objectInstance : autoCompleteController.gd){
                    if(objectInstance.getDescribe().getKeyPrefix() == sObjectId.subString(0,3)){    
                        return objectInstance.getDescribe().getLabel();
                    }
                }
            }
            return null;
        }
    }
}

Visualforce Page

<!--
    @Author: Mohammad Usman
    @Version: 1.0
    @Description: Auto complete and search funtionalty for the Account,Lead,Opportunity and Contact objects.
-->
<apex:page sidebar="false" controller="autoCompleteController" showHeader="false" >   
    <apex:stylesheet value="{!UrlFor($Resource.jQuery181,'/jquery/jquery-ui.css')}"/>
    <apex:includeScript value="{!UrlFor($Resource.jQuery181,'/jquery/jquery-1.8.js')}"/>
    <apex:includeScript value="{!UrlFor($Resource.jQuery181,'/jquery/jquery-ui.js')}"/>
    <style type="text/css">
        .ui-autocomplete-loading { 
            background: white url("{!$Resource.ajaxLoad}") right center no-repeat; 
        }
        .autocomplete{
            border-color: #929292 #D5D5D5 #D5D5D5 #929292 !important;
            border-style: solid;border-width: 1px !important;
            height: 18px !important;
            padding: 3px 10px;width: 534px !important;
        }
        .searchBtn{
            background: #FDCE3E !important;
            border: 1px solid #E5A716 !important;
            color: #434343 !important;
            height: 26px !important;
            margin-left: 7px !important;
            padding: 0 23px !important;
            font-size: 15px !important; 
            font-weight: bold !important;
        }
    </style>  
    <apex:form id="frmId">
        <center>
            <img src="http://dl.dropbox.com/u/29695913/images.jpg" style="float:left;height:50px;width:120px;" />
            <img src="http://dl.dropbox.com/u/29695913/images.jpg" style="float:right;height:50px;width:120px;" /><br />
            <b style="vertical-align: middle;"> Search Record: </b> 
            <apex:inputText id="autocomplete_textbox" styleClass="autocomplete"/> 
            <apex:commandButton value="Search" styleClass="searchBtn" reRender="frmId"/>             
        </center>
        <hr/>
        <apex:inputhidden value="{!searchedRecordId}" id="searchId"/>
        <apex:detail inlineEdit="false" subject="{!searchedRecordId}" showChatter="true" title="true"/>     
        <script type="text/javascript">
            //To remove conflictions of jquery
            var j$ = jQuery.noConflict();            
            j$(document).ready(function() {
                //Auto complete textbox component
                var autoCompleteTextBoxId = "input[id$='autocomplete_textbox']";
                var queryTerm;
                //auto complete method
                j$(autoCompleteTextBoxId).autocomplete({
                    minLength: 2,
                    source: function(request, response) {
                                queryTerm = request.term;
                                var sObjects;
                                var searchText = j$(autoCompleteTextBoxId).val();
                                AutoCompleteController.getRecords(searchText,function(result, event){
                                    if(event.type == 'exception') {
                                          alert(event.message);
                                    } else {
                                         sObjects = result;
                                         response(sObjects);
                                    }
                                });
                           },
                    focus: function( event, ui ) {
                            j$(autoCompleteTextBoxId).val( ui.item.Name );
                            return false;
                            },
                    select: function( event, ui ) {
                                j$(autoCompleteTextBoxId).val( ui.item.Name );                                
                                j$("input[id$='searchId']").val(ui.item.Id);
                                return false;
                            },
                 })
                 .data( "autocomplete" )._renderItem = function( ul, item ) {
                    var entry = "<a>" + item.Name;
                    j$.each("".split(",") , function(key, value) {
                        entry = entry + " <b>" + item.sObjectName +"</b>";
                    });
                    entry = entry + "</a>";
                    entry = entry.replace(queryTerm, "<b>" + queryTerm + "</b>");
                    return j$( "<li></li>" )
                        .data( "item.autocomplete", item )
                        .append( entry )
                        .appendTo( ul );
                };
            });            
        </script>
    </apex:form>
</apex:page>
Output of the above code

References
http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_sosl_about.htm
http://jquery.com/

[repost ]How to insert/upload new Products, Price Books & Opportunity Line Item via Data Loader?

original:https://help.salesforce.com/HTViewSolution?id=000025289&language=en_US

Knowledge Article Number: 000025289 

Description

How to insert/upload new Products, Price Books & Opportunity Line Item via Data Loader?

Resolution

Questions this solution will help answer:
– Step 1: How do I create and activate a Product Price Book?
– Step 2: How do I load Products via the Data Loader?
– Step 3: How do I load Products into Standard Price Book and Custom Price Books via the Data Loader?
– Step 4: How do I load Opportunity Line Items via the Data Loader?

The following steps need to be taken in the exact order:

_______________________________________________________________________________________________
How do I create and activate a Product Price Book?

Step 1 – SET UP Price Books in Salesforce CRM
Usually there are only a small finite set of price books, therefore this action can be done directly in the application. If you choose to use the Data Loader, use the “PriceBook2” table.

1. In salesforce click on the Products Tab.
2. In the bottom right-hand corner, click on “Manage Price Books”
3. Add as many Price Books as necessary.
4. The Standard Price Book MUST be active.

_______________________________________________________________________________________________
How do I load Products via the Data Loader?

Step 2a – PREPARE Products file
(See attached template “Products_INSERT_Template_Step2a.csv”)

1. Make sure all Products have a Product Name
-This is a required field when loading Products into the “Products2” Table
2. If you use Record Types, you will need the “RecordTypeID” for the insert file
-EXPORT “Record Type” table via the Force.com AppExchange Data Loader (you will need to check “Show all Sforce Objects”). This export will provide the 18 digit ID, which is needed for the insert.
3. Make sure your column headers in the file exactly match fields in SFDC

Step 2b – INSERT Products to the “Products2” Table.

1.Open Data Loader
2. Insert to “Products2” table
3. Save success and error files in a folder for this project, you will use them in the Step 3b.

_______________________________________________________________________________________________
How do I load Products into Standard Price Book and Custom Price Books via the Force.com AppExchange Data Loader?

Step 3a – EXPORT “Price Book 2” Table
You will need the “PriceBook2ID’s” and other fields to prepare a file for the next step.

1. Open the Data Loader
2. Export the “Price Book 2” table
3. You need “Price Book 2 ID” for insert file
4. Save file in an easy to access location
5. This will be used to create your final insert file for Step 3c and 3e

Step 3b – PREPARE file for Standard Price Book

NOTE: Even if you do not want to associate these Products with the Standard Price Book this step still must be followed.

Data Columns:
-Product2ID: Use the “ID” from the “Product2” success file from Step 2
-PriceBook2ID: get the Standard Price Book ID from file created in Step 3a
-CurrencyIsoCode: use Salesforce CRM currency symbol (only for Multi-Currency org)
-Unit Price: this is the List Price/Standard Price field for the product
-UseStandardPrice: this should be TRUE or FALSE and indicates whether to use the price from Standard Price Book or not.
—-FALSE (only option for Standard Price Book)
—-TRUE (is not an option because “Use Standard Price” field is not available in the Standard Price Book)
-IsActive: TRUE (must be TRUE to insert – this can be changed to FALSE after Step 5 is complete)

Step 3c – INSERT into “Price Book Entry” Table

1. Open the Data Loader
2. Insert to “Price Book Entry” table
3. Save success and error files in a folder for this project

Step 3d – PREPARE file for Custom Price Books
Use the file from last step and modify it to have the Custom Price Book ID.

Data Columns:
-Product2ID: Use the “ID” from the “Product2” success file from Step 2
-PriceBook2ID: get the Custom Price Book ID from file created in Step 3a
-CurrencyIsoCode: use Salesforce currency symbol (only for Multi-Currency org)
-Unit Price: this is the List Price/Standard Price field for the product
-UseStandardPrice: this should be TRUE or FALSE and indicates whether to use the price from Standard Price Book or not.
—-FALSE (this choice means you will use the Unit Price from the custom Price Book and NOT the Unit Price from the Standard Price Book)
—-TRUE (this option only works if this Product has been added to the Standard Price Book. This choice will use the Unit Price from the Standard Price Book)
-IsActive: TRUE

Step 3e – INSERT into “Price Book Entry” Table
MAKE SURE your Custom Price Books are Active in SFDC.

1. Open Data Loader
2. Insert to “Price Book Entry” table
3. Save success and error files in a folder for this project

Step 3f (optional)
INACTIVATE Standard Products in Standard Price Book

1. Take the success files the Standard Price Book Entry insert from step 3c
2. Change the values in “IsActive” from true to FALSE
3. Map only the “Product2ID” and “IsActive” fields
4. Open AppExchange Data Loader
5. UPDATE “Price Book Entry” Table
6. Save success and error files in a folder for this project

_______________________________________________________________________________________________
How do I load Opportunity Line Items via the Data Loader?

Step 4 (optional)
INSERT products into “OpportunitiesLineItem” Table

1. Data Columns:
-Opportunity ID
-PricebookEntryID
-Total Price
-Quantity
-Unit price
-other fields as needed
NOTE: The Opportunity Product Table provides the “ProductID” as a mappable field, however this field should not be mapped in this process.

1. Export the Opportunity Product (OpportunityLineItem) table for a template
2. Export the appropriate Opportunities from the data loader with filter criteria.
3. Copy OpportunityID into the insert/update template file.
4. Export the “Price Book Entry” table from the data loader, the “ID” column is the PricebookEntryID.
5. Copy the appropriate PricebookEntryID’s (with corresponding Opportunities) into the insert/update file.
6. Add the other data into the relevant columns in the insert/update file.
7. Open AppExchange Data Loader.
8. Insert to “Opportunity Product (OpportunityLineItem)” table

_______________________________________________________________________________________________

NOTES:
– The PricebookEntryID used in the insert file must correspond to the Opportunity Price Book that has been selected in the application.
– If you change the Product Family as a part of the update process, be sure to change the picklist values for this standard field as well.
– When loading new products and Price Books, pay particular attention to the sequence of steps detailed above. The user defined standard Price Book must contain all products before the products can be assigned to other Price Books.

ADDITIONAL REFERENCE:

1. Use Link: http://www.sforce.com/us/docs/sforce60/wwhelp/wwhimpl/js/html/wwhelp.htm
2. From the Table of Contents | Entity Relationships Diagrams | Product and Schedule objectspdf

How can you view the Opportunity Line Item ID and the Pricebook ID on an Opportunity with Product report?

https://help.salesforce.com/apex/HTViewSolution?urlname=How-can-you-view-the-Opportunity-Line-Item-ID-and-the-Pricebook-ID-on-an-Opportunity-with-Product-report-1327109401509&language=en_US

[repost ]Insert New LineItem Records via VF/Apex using Wrapper

original:http://boards.developerforce.com/t5/Visualforce-Development/Insert-New-LineItem-Records-via-VF-Apex-using-Wrapper/m-p/505909#M56217

Custom Object which operates very similarly to OLI – trying to create new line items based on returned results. Able to get 90% there, but the insertion of the records is killing me.  I know I’m simply missing the obvious – code below; Any/all assistance greatly appreciated.

 

Separate WrapperClass

public class AssetWrapper {
	public SiteAsset__c asset {get;set;}
	public Boolean selected {get;set;}	

	public AssetWrapper(SiteAsset__c s) {
		asset = s;
		selected = false;
	}
}

 

 

Controller:

 

public with sharing class NewProductController {
	public String siteid {get;set;}
	public Opportunity opportunity;
	public ID opId {get; set;} 
	public SiteAsset__c siteasset;
	//OpportunityAssetLine__c lineItem;

	public OpportunityAssetLine__c lineItem {get;set;}

	public OpportunityAssetLine__c getOli(){
		if(lineItem == null) lineItem = new OpportunityAssetLine__c();
		return lineItem;
	}

	public List<SiteAsset__c> results = new List<SiteAsset__c>(); 		// Result List - productList
	public List<SiteAsset__c> selProduct = new List<SiteAsset__c>();  	// Result List - selectedAsset

	public List<AssetWrapper> productList {get;set;}					// All Site Assets
	public List<AssetWrapper> selectedAsset {							// Selected Assets
		get {
			if(selectedAsset == null) selectedAsset = new List<AssetWrapper>();
			return selectedAsset;
		}
		set;
	}
	public List<AssetWrapper> selectedOli {
		get {
			if(selectedOli == null) selectedOli = new List<AssetWrapper>();
			return selectedOli;
		}
		set;
	}	

	// Constructor set Opportunity Id, Site
  	public NewProductController() {
    	opportunity = [SELECT Id, Site__r.Id FROM Opportunity WHERE Id = :ApexPages.currentPage().getParameters().get('oid')];    //:System.currentPageReference().getParameters().get('id')];
  		this.lineItem = new OpportunityAssetLine__c();
  	}

	public List<AssetWrapper> getProducts(){
		siteid = opportunity.Site__r.Id;
		if(productList == null) {
			productList = new List<AssetWrapper>();
			for(SiteAsset__c s : [SELECT Id, Name, Site__r.Id, Asset__r.Id, Asset__r.Name, Occupancy__c FROM SiteAsset__c WHERE Site__r.Id = :siteid ORDER BY Id]){
				productList.add(new AssetWrapper(s));
			}
		}
		return productList;
	}

	public PageReference next() {
		selectedAsset.clear();	
		for(AssetWrapper aw : productList) {
			if(aw.selected)
			selectedAsset.add(new AssetWrapper(aw.asset)); 
		}

		Integer i = selectedAsset.size();	
		if(i > 0) {			
			return Page.selectedAssets;
		} else {
			ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR, 'Please select a Product/Asset.'));
			return null;
		}				
	}	

	public PageReference save() {
		Integer i = selectedAsset.size();
		Map<OpportunityAssetLine__c, String> test = new Map<OpportunityAssetLine__c,String>();	
		for(Integer a = 0; a < i; a++) {
			lineItem = new	OpportunityAssetLine__c();
			string product = selectedAsset[a].asset.Id;
			string o = opportunity.Id;
			String price = this.lineItem.Price__c;
			String code = this.lineItem.AssetCode__c;
			String lease = this.lineItem.LeaseType__c;
			test.put(lineItem(product, o),Price__c = price, AssetCode__c = code, LeaseType__c = lease);
			}
		PageReference home = new PageReference('/' + opportunity.Id);
		home.setRedirect(true);
		return home;
		return null;
	}

	public PageReference back() {
		return Page.SelectAssetsOpportunity;
	}

}

 

And the VF P1

<apex:page controller="NewProductController" showHeader="true" tabStyle="Opportunity">
	<apex:form >
        <apex:pageBlock >
            <apex:pageBlockButtons >
                <apex:commandButton action="{!next}" value="Select" />
            </apex:pageBlockButtons>
            <apex:pageMessages />
            <apex:pageBlockTable value="{!products}" var="s" id="table">
                <apex:column >
                    <apex:inputCheckbox value="{!s.selected}"/>
                </apex:column>
                <!-- Asset/Product values within Wrapper -->
                <apex:column >
					<apex:inputHidden value="{!s.asset.Asset__r.Id}" />
				</apex:column>
                <apex:column value="{!s.asset.Asset__r.Name}" headerValue="Name" />
                <apex:column value="{!s.asset.Occupancy__c}" />
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

P2

<apex:page controller="NewProductController" showHeader="true" tabStyle="Opportunity">>
	<apex:form >
		<apex:pageBlock >
			<apex:pageBlockButtons >
				<apex:commandButton action="{!back}" value="Back" />
				<apex:commandButton action="{!save}" value="Save" />
			</apex:pageBlockButtons>
		<apex:Messages /> 

		<apex:pageBlockSection columns="1"> 
			<apex:pageBlockTable value="{!selectedAsset}" var="s">
				<apex:column >

					<apex:inputHidden value="{!lineItem.Id}" />
				</apex:column>
				<apex:column style="font-weight: bold;" value="{!s.asset.Asset__r.Name}" />		
				<apex:column style="font-weight: bold;" value="{!s.asset.Name}" />			
				<apex:column headerValue="Product Code">
					<apex:inputField value="{!lineItem.AssetCode__c}" />
				</apex:column>
				<apex:column headerValue="Lease Type" >
					<apex:inputField value="{!lineItem.LeaseType__c}" />
				</apex:column>
				<apex:column headerValue="Dimensions (sq/meters)" >
					<apex:inputField value="{!lineItem.DimensionSqMet__c}" />
				</apex:column>
				<apex:column headerValue="Activation Date">
					<apex:inputField required="true" value="{!lineItem.ActivationDate__c}" />
				</apex:column>
				<apex:column headerValue="Price" >
					<apex:inputField required="true" value="{!lineItem.Price__c}" />
				</apex:column>					
			</apex:pageBlockTable>
		</apex:pageBlockSection>
		</apex:pageBlock>
	</apex:form>
</apex:page>

 

I can insert but am missing a step as I’m not able to ensure proper value pairing (i.e. Price duplicates etc.) so I’m missing the simple one here.  thanks again.

[repost ]A Work-Around for the SFDC Auto Number Dilemma

original:http://techblog.appirio.com/2009/01/work-around-for-sfdc-auto-number.html

A Work-Around for the SFDC Auto Number Dilemma

Joe Krutulis
Some of the trickiest Force.com deployment problems we’ve faced at Appirio have involved custom fields of type Auto Number. Values in these fields cannot be updated without first temporarily changing their type to Text — but if the field is referenced in any Apex code, then the type cannot be changed at all!
Thankfully, dynamic SOQL and the get() and put() methods have made this pain a thing of the past.
To illustrate the technique, create a custom object AutoNumberTest__c with which to experiment. The object should have a single custom field called “autonumber__c” of type Auto Number.
After creating the object, you can experiment to prove that the type of the Autonumber__c field can be freely changed from Auto Number to Text and back to Auto Number again, allowing for necessary updates as part of conversion or recoveries.
As a side note, this does not apply to the standard Name field set as auto number. Name fields are treated differently than custom fields, and the auto number attribute of the name field can be changed without running afoul of the Apex compiler’s type checking.
Now let’s write a trigger after insert or update of an AutoNumberTest__c object that references the autonumber__c column.
The following example code uses the field to create a Note object and attach it to the AutoNumberTest__c object.
 trigger AutoNumberTestMsg
      on AutoNumberTest__c (after insert, after update) {

  List<Note> newNotes = new List<Note>();

  for (AutoNumberTest__c a : Trigger.new ) {

    Note msg = new Note(
      parentId = a.id,
      title = '' + a.autonumber__c,
      body = '' + a
    );
    newNotes.add(msg);
  }
  insert newNotes;
}
This code iterates through all the new values in the trigger batch, creates a new Note object containing a string representation of the AutoNumberTest__c object, and, after looping through all the objects, inserts the new notes in a single DML statement. (The expression '' + a.autonumber__c returns a string representaion of the value autonumber__c field.)
After creating this trigger, attempting to change the column type from Auto Number causes the following error:
Validation Errors While Saving Record(s)
There were custom validation error(s) encountered
while saving the affected record(s). The first validation
error encountered was "Cannot change field type of a
custom field referenced in Apex class or trigger:
AutoNumberTestMsg".
In this particular example, it would be quite easy to comment out the existing code, change the field type to perform maintenance on data values, and then uncomment the code. It’s seldom easy and sometimes nearly impossible when promotion is involved or when the dependent code is more complicated.
The Winter ’09 introduction of the Object.get() and Object.put() methods provides a new alternative. We can change the expression a.autonumber__c to g.get('autonumber__c') and escape the compiler!
The new trigger is as follows:
 trigger AutoNumberTestMsg
      on AutoNumberTest__c (after insert, after update) {

  List<Note> newNotes = new List<Note>();

  for (AutoNumberTest__c a : Trigger.new ) {

    Note msg = new Note(
      parentId = a.id,
      title = '' + a.get('autonumber__c'),
      body = '' + a
    );
    newNotes.add(msg);
  }
  insert newNotes;
}
It is now possible to change the type of the autonumber__c column without generating an error.
Of course, this change exposes us to a new class of run-time errors that otherwise would have been prevented by the compiler. For example, if we now change the API name of the autonumber__c field without also changing the trigger, the code will cause a run-time exception.
In more realistic scenarios, the changes required are usually more complicated and involve more significant risks of run-time problems.
For example, if we use a SOQL expression such as:
List<AutoNumberTest__c> aList =
    [select id, autonumber__c from AutoNumberTest__c];
We’ll encounter the same compiler-enforced restriction on changing the type of the autonumber field.
We need to resort to dynamic SOQL and the Database.query() method to avoid the direct compiler reference to the field.
List<AutoNumberTest__c> aList =
    Database.query('select id, autonumber__c from AutoNumberTest__c');
Switching to dynamic soql queries can be unwieldy and could also be more difficult to maintain. (With the Spring ’09 release, we gain the ability to use ‘:’ interpolation in dynamic SOQL, which will make things a little easier.) However, in cases involving the need to temporarily change an Auto Number field to allow for updates, switching to dynamic SOQL will be far, far easier than some of the horrendous contortions that have been required in the past!

[repost ]Elegant way to convert Set into String for Dynamic SOQL IN comparison

original:http://salesforce.stackexchange.com/questions/3526/elegant-way-to-convert-setid-into-string-for-dynamic-soql-in-comparison

When I have a Set of Id’s and I want to do a dynamic SOQL query that I want to use it in for an IN comparison, I have to convert the Set to string in the format of:

('id1','id2',id3',...)

The way I do it works, but is not particularly elegant, so I’m wondering if anyone has come up with a better way. Am I missing something? Is there something out there that can just turn a set or list into the properly formatted string?

Here’s my code (yes, my example isn’t dynamic SOQL, but I’ve used this several times in dynamic SOQL):

Map<Id,Account> accts = new Map<Id,Account>([select Id from Account]);

String idString = '(\'';
for (Id thisId : accts.keyset()) {
    idString += thisId + '\',\'';
}
idString = idString.substring(0,idString.length()-2); //<--this is the bit I hate doing
idString += ')';

String q = 'select id from Contact where AccountId in ' + idString;

List<Contact> cts = Database.query(q); 

answer:

Believe it or not bind variables actually work in dynamic SOQL:

Map<Id,Account> accts = new Map<Id,Account>([select Id from Account]);
Set<Id> accountIds = accts.keySet();
String q = 'select id from Contact where AccountId in :accountIds';
List<Contact> cts = Database.query(q);

Note that you can’t embed method calls (e.g. AccountId in :accts.keySet() would fail) when binding in this way.

This is a very, very good way of doing things as it offers a massive reduction in script statements, heap space, and even added security by automatically escaping input. I believe it also means that the bind data doesn’t count against the 10k SOQL query character limit.

I can confirm there are a number of ISV apps that are dependent on this so there’s no way salesforce could remove it in a future version without a massive outcry. This method is actually salesforce endorsed!

Also, see this question if you’re having trouble with more complex uses, in short: you can only use an in-scope variable, not an actual expression with these binds.

[repost ]Efficient way to find error causing “Save Error: Entity is not org-accessible”

original:http://salesforce.stackexchange.com/questions/4599/efficient-way-to-find-error-causing-save-error-entity-is-not-org-accessible

Question:

I’ve ran into this issue once or twice before. The problem manifests itself as this in the Force.com IDE:

enter image description here

The way I found the issue was by commenting out large swathes of code, and then saving the class to the server again until I narrow it down to the right section. This is quite time consuming.

The actual issue in the code was this, where I’m referencing an object that isn’t in the scope of the method:

enter image description here

Obvious once you spot it, I would have expected it to give me an error on the line saying “Save error: Method does not exist or incorrect signature: invoices.getSObject(String)” but it doesn’t. Interestingly, if I create another dummy line (below), which you would think would behave the same I do get the correct error:

SObject test = instanceoutofscope.getSObject( 'c2g__Account__r' );

Does anyone know a) the specific conditions in which this error occurs (it seems unpredictable to me), and b) of a more efficient way of detecting it?

Thanks for reading!

Answer:

According to the Apex developer’s guide:

If the expression ends with a set of parentheses (for example, name1.name2.[…].nameM.nameN()), the Apex parser evaluates the expression as follows:

The parser first assumes that name1 is a local variable with name2 – nameM as field references, and nameN as a method invocation.
If the first assumption does not hold true:
• If the expression contains only two identifiers (name1.name2()), the parser then assumes that name1 is a class name and name2 is a method invocation.

• If the expression contains more than two identifiers, the parser then assumes that name1 is a class name, name2 is a static variable name with name3 – nameM as field references, and nameN is a method invocation.
If the second assumption does not hold true, the parser then assumes that name1 is a namespace name, name2 is a class name, name3 is a static variable name, name4 – nameM are field references, and nameN is a method invocation.
If the third assumption does not hold true, the parser reports an error.
In your case the parser has made it to point 4, but hasn’t reported a particularly helpful error. It seems likely that the assumption is that invoice is an sobject or class, and that getSObject() is a static method on this sobject/class. Rather than tell you that this sobject/class doesn’t exist, it falls back on the assumption that this must be something that just happens not to be visible to your org configuration or license. I think that in the majority of cases the errors reported in this situation are likely to be unhelpful as the parser fundamentally can’t determine what you are trying to do, so it has a one size fits all message.

The first few times this happened to me it was with sobject names (Quote was one) so I assumed it that was related to the use of a real sobject name, but I’ve also had it happen where I’ve missed out the namespace for a system class (e.g. Action rather than ApexPages.Action).

The biggest problem when I’ve hit this is that a lot of the time the error is reported as ‘line 1 column 8’ or is missing completely. If that is the case I just go back over my recent changes (using the Force.com IDE) compare with and try to narrow down the areas to look in, which isn’t really any different to what you are doing.

[repost ]A Utility Apex Class to Convert All Types Into String

original:http://salesforcesource.blogspot.com/2008/11/utility-apex-class-to-convert-all-types.html

If you have been in the programming world long enough, you already know that casting types from one to another specially string representation of the variables is always part the job.

Whether you want to show the data to user or form the variables in different formats, incorporate them in messages, etc.

In this article I will present you one of the Apex classes I have written to help me faster develop Salesforce Applications.

This class helps me in number of ways, for example I do not need to worry about the underlying Apex code to convert a primitive type to string anymore, for all types I just need to call one method “ToString” and it will take care of for me.
Also the methods provide me with formatting capabilities, so I can not only convert to string but also format the string in many ways I need.

Look at the below code and see how the results are:

ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Integer: '+ zConvert.ToString(13434)));
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Double: '+ zConvert.ToString(1.23)));
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Boolean: '+ zConvert.ToString(true)));
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Date: ' + zConvert.ToString(date.newinstance(1960, 2, 17))));
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Date time: ' + zConvert.ToString(Datetime.now(),'MMM, dd yyyy')));
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'File Size: '+ zConvert.FileSizeToString(6766767)));
 ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Money: '+ zConvert.CurrencyToString(Decimal.valueOf(34.99),'$'))

 

Well, very nice, now let’s see how the actual class is developed:
Because I would like to directly call my Coverter’s class methods without creating a new instance of the class, I have defined all the methods as “static”.

The methods for this class are:

  • ToString(Integer)
  • ToString(Double)
  • ToString(Long)
  • ToString(Boolean)
  • ToString(Date)
  • ToString(Date,format)
    sample: zConvert.ToString(mydate,’MM-dd-yy’)
  • ToString(Time)
  • ToString(Time,format)
    sample: zConvert.ToString(myTime,’hh:mm:ss’)
  • ToString(Datetime)
  • ToString(Datetime,format)
  • ToString(Decimal)
  • ToString(Decimal, ScientificNotaion)
    ScientificNotaion is a Boolean value and if false is passed then the string will not have scientific notations.
  • FileSizeToString(Long)
    Returns values such as “5.5 KB”, “8 MB”, etc. Parameter passed is in bytes.
  • CurrencyToString(Decimal, CurrencyChar)
    CurrencyChar can be “$”, “£”, etc

 

public class zConvert
{
 /* The Initial Developer of the Original Code is Sam Arjmandi.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved. 
 * 
 * This Code is provided "As Is" without warranty of any kind.
 */
 
  public static String ToString(integer Value)
  {
      /* string representation if an Integer value */
      return Value.format();
  }
 
  public static String ToString(Double Value)
  {
    /* string representation if a Double value */
     return Value.format();
  }
 
  public static String ToString(Boolean Value)
  {
     /* string representation if a Boolean value */
     if (Value)
       return 'true';
     else
       return 'false';
  }
 
  public static String ToString(Long Value)
  {
    /* string representation if a Long value */
    return Value.format();
  }
 
  public static String ToString(Date Value)
  {
     /* string representation if a Date value */
     return Value.format();
  }
 
  public static String ToString(Date Value,String format)
  {
    /* string representation if a Date value with formatting */
    Datetime temp = Datetime.newInstance(Value.year(), Value.month(), Value.day());
    return temp.format(format);
  }
 
  public static String ToString(Datetime Value)
  {
     /* string representation if a Datetime value */
     return Value.format();
  }
 
  public static String ToString(Datetime Value,String format)
  {
     /* string representation if a Datetime value with formatting */
     return Value.format(format);
  }
 
  public static String ToString(Time Value)
  {
    /* string representation if a Time value */
    return String.valueOf(Value);
  }
 
  public static String ToString(Time Value, String format)
  {
    /* string representation if a Time value with formating */
    Datetime temp = Datetime.newInstance(1970, 1, 1, Value.hour(), Value.minute(), Value.second());
    return temp.format(format);
  }

  public static String ToString(Decimal Value)
  {
    /* string representation if a Decimal value */
    return Value.format();
  }
 
  public static String ToString(Decimal Value, Boolean ScientificNotation)
  {
    /* string representation if a Decimal value with or without Scientific Notation */
    if (ScientificNotation)
     return Value.format();
    else
     return Value.toPlainString();
  }
 
  public static String FileSizeToString(Long Value)
  {
     /* string representation if a file's size, such as 2 KB, 4.1 MB, etc */
     if (Value < 1024)
       return ToString(Value) + ' Bytes';
     else
     if (Value >= 1024 && Value < (1024*1024))
     {
       //KB
       Decimal kb = Decimal.valueOf(Value);
       kb = kb.divide(1024,2);
       return ToString(kb) + ' KB';
     }
     else
     if (Value >= (1024*1024) && Value < (1024*1024*1024))
     {
       //MB
       Decimal mb = Decimal.valueOf(Value);
       mb = mb.divide((1024*1024),2);
       return ToString(mb) + ' MB';
     }
     else
     {
       //GB
       Decimal gb = Decimal.valueOf(Value);
       gb = gb.divide((1024*1024*1024),2);
      
       return ToString(gb) + ' GB';
     }
    
  }
 
  public static String CurrencyToString(Decimal Value, String CurrencyChar)
  {
     return CurrencyChar + ToString(Value);
  }
 
}