Integrating Google Maps in Salesforce
Requirement is to show the location of my Account on Google Maps. It looks difficult but actually it is very simple to implement.
Just create a new visualforce page with this code
<apex:page standardController="Account">
<script src="http://maps.google.com/maps?file=api">
</script>
<script type="text/javascript">
var map = null;
var geocoder = null;
var address = "{!Account.BillingStreet}, {!Account.BillingPostalCode} {!Account.BillingCity}, {!Account.BillingState}, {!Account.BillingCountry}";
function initialize() {
if(GBrowserIsCompatible())
{
map = new GMap2(document.getElementById("MyMap"));
map.addControl(new GMapTypeControl());
map.addControl(new GLargeMapControl3D());
geocoder = new GClientGeocoder();
geocoder.getLatLng(
address,
function(point) {
if (!point) {
document.getElementById("MyMap").innerHTML = address + " not found";
} else {
map.setCenter(point, 13);
var marker = new GMarker(point);
map.addOverlay(marker);
marker.bindInfoWindowHtml("Account Name : <b><i> {!Account.Name} </i></b>
Address : "+address);
}
}
);
}
}
</script>
<div id="MyMap" style="width:100%;height:300px"></div>
<script>
initialize() ;
</script>
</apex:page>
As you can see that I have used standard controller so you need to pass the account Id in URL. Let’s say if the page name is “GoogleMaps” then the URL will look something like this : “…/apex/GoogleMaps?id=YOUR_ACCOUNT_ID”.

Use this page as in line visualforce page on native layout and enjoy the Google maps with Salesforce.
References
http://code.google.com/apis/maps/documentation/webservices/
http://code.google.com/apis/maps/index.html
Original Post by Forceguru : http://forceguru.blogspot.com/2012/01/integrating-google-maps-in-salesforce.html
Displaying Role Hierarchy on Visualforce Page – Tree View
This is a cross post from my Blog.
Ever thought of building a tree like data structure for the users based on role hierarchy and displaying it in the form of a JavaScript tree with node selection capability on the Visualforce page?
So recently I came across a functionality where a third party javascript calendar was used on the VisualForce page and all events were fetched programmatically through Apex and plotted on the calendar. The UI looked good along with other custom developed functionality. All was fine until client asked if it was possible to select some of the logged in user’s subordinates through custom VF page and plot events on the calendar for the selected users only. In other words, fetch and display only those events which were owned by users who worked below the logged in user in the role hierarchy.
It got me thinking and I did some research to check if there was an easier way to get this done, but soon realized that this required custom and tricky Apex/VF code. There’s a nice little script written by Jeff Douglas that was closest to what I actually wanted.
So I came up with this handy utility which fulfils my requirements. Getting user IDs of subordinates could also be useful in situations where, for example, you want to do a comparative analysis of performance for all users reporting to a manager.

There are mainly two parts to the solution I designed:
1. RoleUtil (Apex Class): Utility class which exposes the following API
a. public static RoleNodeWrapper getRootNodeOfUserTree (Id userOrRoleId) – function creates the tree data structure for the requested user or role ID and returns the root node to the caller
b. public static ListgetAllSubordinates (Id userId) – function returns the list of all subordinate users for the requested user ID
c. public static String getTreeJSON (Id userOrRoleId) – function returns the JSON string for the requested user or role ID
d. public static String getSObjectTypeById(Id objectId) – general utility function to return the string representation of the object type for the requested object ID
e. public static Boolean isRole (Id objId) – internally uses the getSObjectTypeById (#d above) to check whether the requested object ID is of UserRole type
f. public class RoleNodeWrapper (inner class) – wrapper for user role, represents a node in the tree data structure mentioned above and exposes boolean properties like hasChildren, hasUsers, isLeafNode, etc
public class RoleUtil {
/********************* Properties used by getRootNodeOfUserTree function - starts **********************/
// map to hold roles with Id as the key
private static Map roleUsersMap;
// map to hold child roles with parentRoleId as the key
private static Map > parentChildRoleMap;
// List holds all subordinates
private static List allSubordinates {get; set;}
// Global JSON generator
private static JSONGenerator gen {get; set;}
/********************* Properties used by getRootNodeOfUserTree function - ends **********************/
/********************* Properties used by getSObjectTypeById function - starts ********************* */
// map to hold global describe data
private static Map gd;
// map to store objects and their prefixes
private static Map keyPrefixMap;
// to hold set of all sObject prefixes
private static Set keyPrefixSet;
/********************* Properties used by getSObjectTypeById function - ends **********************/
/* // initialize helper data */
static {
// initialize helper data for getSObjectTypeById function
init1();
// initialize helper data for getRootNodeOfUserTree function
init2();
}
/* // init1 starts */
private static void init1() {
// get all objects from the org
gd = Schema.getGlobalDescribe();
// to store objects and their prefixes
keyPrefixMap = new Map{};
//get the object prefix in IDs
keyPrefixSet = gd.keySet();
// fill up the prefixes map
for(String sObj : keyPrefixSet) {
Schema.DescribeSObjectResult r = gd.get(sObj).getDescribe();
String tempName = r.getName();
String tempPrefix = r.getKeyPrefix();
keyPrefixMap.put(tempPrefix, tempName);
}
}
/* // init1 ends */
/* // init2 starts */
private static void init2() {
// Create a blank list
allSubordinates = new List();
// Get role to users mapping in a map with key as role id
roleUsersMap = new Map([select Id, Name, parentRoleId, (select id, name from users) from UserRole order by parentRoleId]);
// populate parent role - child roles map
parentChildRoleMap = new Map >();
for (UserRole r : roleUsersMap.values()) {
List tempList;
if (!parentChildRoleMap.containsKey(r.parentRoleId)){
tempList = new List();
tempList.Add(r);
parentChildRoleMap.put(r.parentRoleId, tempList);
}
else {
tempList = (List)parentChildRoleMap.get(r.parentRoleId);
tempList.add(r);
parentChildRoleMap.put(r.parentRoleId, tempList);
}
}
}
/* // init2 ends */
/* // public method to get the starting node of the RoleTree along with user list */
public static RoleNodeWrapper getRootNodeOfUserTree (Id userOrRoleId) {
return createNode(userOrRoleId);
}
/* // createNode starts */
private static RoleNodeWrapper createNode(Id objId) {
RoleNodeWrapper n = new RoleNodeWrapper();
Id roleId;
if (isRole(objId)) {
roleId = objId;
if (!roleUsersMap.get(roleId).Users.isEmpty()) {
n.myUsers = roleUsersMap.get(roleId).Users;
allSubordinates.addAll(n.myUsers);
n.hasUsers = true;
}
}
else {
List tempUsrList = new List();
User tempUser = [Select Id, Name, UserRoleId from User where Id =: objId];
tempUsrList.add(tempUser);
n.myUsers = tempUsrList;
roleId = tempUser.UserRoleId;
}
n.myRoleId = roleId;
n.myRoleName = roleUsersMap.get(roleId).Name;
n.myParentRoleId = roleUsersMap.get(roleId).ParentRoleId;
if (parentChildRoleMap.containsKey(roleId)){
n.hasChildren = true;
n.isLeafNode = false;
List lst = new List();
for (UserRole r : parentChildRoleMap.get(roleId)) {
lst.add(createNode(r.Id));
}
n.myChildNodes = lst;
}
else {
n.isLeafNode = true;
n.hasChildren = false;
}
return n;
}
public static List getAllSubordinates(Id userId){
createNode(userId);
return allSubordinates;
}
public static String getTreeJSON(Id userOrRoleId) {
gen = JSON.createGenerator(true);
RoleNodeWrapper node = createNode(userOrRoleId);
gen.writeStartArray();
convertNodeToJSON(node);
gen.writeEndArray();
return gen.getAsString();
}
private static void convertNodeToJSON(RoleNodeWrapper objRNW){
gen.writeStartObject();
gen.writeStringField('title', objRNW.myRoleName);
gen.writeStringField('key', objRNW.myRoleId);
gen.writeBooleanField('unselectable', false);
gen.writeBooleanField('expand', true);
gen.writeBooleanField('isFolder', true);
if (objRNW.hasUsers || objRNW.hasChildren)
{
gen.writeFieldName('children');
gen.writeStartArray();
if (objRNW.hasUsers)
{
for (User u : objRNW.myUsers)
{
gen.writeStartObject();
gen.writeStringField('title', u.Name);
gen.writeStringField('key', u.Id);
gen.WriteEndObject();
}
}
if (objRNW.hasChildren)
{
for (RoleNodeWrapper r : objRNW.myChildNodes)
{
convertNodeToJSON(r);
}
}
gen.writeEndArray();
}
gen.writeEndObject();
}
/* // general utility function to get the SObjectType of the Id passed as the argument, to be used in conjunction with */
public static String getSObjectTypeById(Id objectId) {
String tPrefix = objectId;
tPrefix = tPrefix.subString(0,3);
//get the object type now
String objectType = keyPrefixMap.get(tPrefix);
return objectType;
}
/* // utility function getSObjectTypeById ends */
/* // check the object type of objId using the utility function getSObjectTypeById and return 'true' if it's of Role type */
public static Boolean isRole (Id objId) {
if (getSObjectTypeById(objId) == String.valueOf(UserRole.sObjectType)) {
return true;
}
else if (getSObjectTypeById(objId) == String.valueOf(User.sObjectType)) {
return false;
}
return false;
}
/* // isRole ends */
public class RoleNodeWrapper {
// Role info properties - begin
public String myRoleName {get; set;}
public Id myRoleId {get; set;}
public String myParentRoleId {get; set;}
// Role info properties - end
// Node children identifier properties - begin
public Boolean hasChildren {get; set;}
public Boolean isLeafNode {get; set;}
public Boolean hasUsers {get; set;}
// Node children identifier properties - end
// Node children properties - begin
public List myUsers {get; set;}
public List myChildNodes {get; set;}
// Node children properties - end
public RoleNodeWrapper(){
hasUsers = false;
hasChildren = false;
}
}
}
2. TreeView (Visualforce component): Dynatree based reusable VF component that exposes input parameters like
a. roleOrUserId – required string type input attribute
b. selectable – boolean attribute to indicate whether you want to display checkboxes against nodes in the tree for user selection
c. JsonData – optional string type input attribute, if supplied to the component ignores the “roleOrUserId” attribute and displays the tree structure for the input JSON string
d. value – a string type output attribute which returns the IDs/Keys of the selected nodes in the CSV format, which can then be utilised by the page controller
<apex:component controller="TreeViewController">
<apex:attribute name="roleOrUserId" required="true" type="String" assignTo="{!roleOrUserId}" description="Enter Role or User Id to build the hierarchy. Pass null if you are passing JSON data as a parameter" />
<apex:attribute name="selectable" type="Boolean" assignTo="{!selectable}" description="Do you want nodes to be selectable?" />
<apex:attribute name="value" type="String" description="IDs of selected Nodes in CSV format" />
<apex:attribute name="JsonData" type="String" assignTo="{!JsonData}" description="JSON input for the tree component" />
<apex:inputHidden id="selectedKeys" value="{!value}" />
<apex:includeScript value="{!URLFOR($Resource.DynaTree, 'jquery/jquery.js' )}" />
<apex:includeScript value="{!URLFOR($Resource.DynaTree, 'jquery/jquery-ui.custom.js' )}" />
<apex:includeScript value="{!URLFOR($Resource.DynaTree, 'jquery/jquery.cookie.js' )}" />
<apex:includeScript value="{!URLFOR($Resource.DynaTree, 'src/jquery.dynatree.js' )}" />
<apex:stylesheet value="{!URLFOR($Resource.DynaTree, 'src/skin/ui.dynatree.css')}" />
<!-- Add code to initialize the tree when the document is loaded: -->
<script type="text/javascript">
$(function(){
// Attach the dynatree widget to an existing <div id="tree"> element
// and pass the tree options as an argument to the dynatree() function:
$("#tree").dynatree({
onActivate: function(node) {
// A DynaTreeNode object is passed to the activation handler
// Note: we also get this event, if persistence is on, and the page is reloaded.
//alert("You activated " + node.data.key);
},
persist: false,
checkbox: {!selectable},
generateIds: false,
classNames: {
checkbox: "dynatree-checkbox",
expanded: "dynatree-expanded"
},
selectMode: 3,
children: {!JsonString},
onSelect: function(select, node) {
// Get a list of all selected nodes, and convert to a key array:
var selKeys = $.map(node.tree.getSelectedNodes(), function(node){
return node.data.key;
});
jQuery(document.getElementById("{!$Component.selectedKeys}")).val(selKeys.join(", "));
// Get a list of all selected TOP nodes
var selRootNodes = node.tree.getSelectedNodes(true);
// ... and convert to a key array:
var selRootKeys = $.map(selRootNodes, function(node){
return node.data.key;
});
},
});
});
</script>
<!-- Add a <div> element where the tree should appear: -->
<div id="tree"> </div>
</apex:component>
You can see a working demo of the functionality here: http://treeview-developer-edition.ap1.force.com/
The code is available as unmanaged package (https://login.salesforce.com/packaging/installPackage.apexp?p0=04t90000000LlqQ) if you want to use it in your org. The code has been written assuming positive use cases and exceptional situations have not much been handled. It is advised to review and tweak the code before you use it in your org.
Apex – Sorting a map
So this is going to be one of the more apex heavy posts. This is a challenge I think many developers
have come across, and while what I propose is by no means the most elegant thing ever, it does do the
job until hopefully salesforce implements a native map sorting method. So here is the basic approach
1) populate map with information
2) create another map with the keys as the values you want to sort by, and the value as the key
for the other map.
3) create a list whos values are the keyset of the map created in step 2
4) sort the list
5) iterate over the list which now has your values in order, get the value from map 2 using the
current loop iterator as the key.
Sounds pretty complicated eh? It’s not SO bad once you kinda get the hang of it, but there is one
gotcha that kind of sucks, but we’ll cover that in a minute.
Here is a sample using a simple object called contestEntry. We’ll create a bunch of them with
random ordering, and then sort them and loop over the sorted result. You should be able to run this
in any org so you can see the principals in action.
public class contestEntry
{
public decimal rank{get;set;}
public string name{get;set;}
}
map<string,contestEntry> entries = new map<string,contestEntry>();
contestEntry entry1 = new contestEntry();
entry1.rank = 5;
entry1.name = 'Frank';
entries.put(entry1.name,entry1);
contestEntry entry2 = new contestEntry();
entry2.rank = 3;
entry2.name = 'Bob';
entries.put(entry2.name,entry2);
contestEntry entry3 = new contestEntry();
entry3.rank = 1;
entry3.name = 'Jones';
entries.put(entry3.name,entry3);
contestEntry entry4 = new contestEntry();
entry4.rank = 4;
entry4.name = 'Sandy';
entries.put(entry4.name,entry4);
contestEntry entry5 = new contestEntry();
entry5.rank = 2;
entry5.name = 'Felix';
entries.put(entry5.name,entry5);
//oh no, these entries are all out of order.
system.debug(entries) ;
//lets get sorting these guys. First we'll need a map to store the rank, and the contestEntry that rank is
//associated with
map<decimal,string> rankToNameMap = new map<decimal,string>();
for(contestEntry entry : entries.values())
{
rankToNameMap.put(entry.rank,entry.name);
}
//now lets put those ranks in a list
list<decimal> ranksList = new list<decimal>();
ranksList.addAll(rankToNameMap.keySet());
//now sort them
ranksList.sort();
//ok, so now we have the ranks in order, we need to figure out who had that rank
for(decimal rank : ranksList)
{
String thisEntryName = rankToNameMap.get(rank);
contestEntry thisEntry = entries.get(thisEntryName);
system.debug(thisEntry);
}
Walking through it first we just make a sample object to use here. Normally this would be whatever you are actually trying to sort, but for the sake of easyness I just created an object called contestEntry. It just holds a name and a rank.
So then I make a map of those things, keyed by the persona name, and containing the contestEntry object. You might in real life have a map of sObjects keyed by their Id and containing the sObject itself. So then I make a bunch of those and add them to the map in random order so my sorting actually has some work to do
The next thing is creating a map with the key of the value I want to sort this list by. The value is key of the first map. So in real life this might be a dollar amount on an opportunity and then the opportunity Id if the original map was a list of opportunities keyed by their Id.
We loop over all the objects in the original map, and add them to our temporary sorting map, again keyed by value to sort by, and value with key from original map.
Then we create a list of the type of the key of the temporary sorting map. Your key was a decimal? Then your list is of decimals as well, etc. Then add all the keys from the sorting map to the list you just made.
Sort the list.
Iterate over the sorted list, each entry in this list will be a key you can use to get the entry from the sorting map, which will contain the key to the original map. You now have a reference to your original map value by whatever value you sorted on.
There is however one gotcha with this approach. If you have duplicates in the value you are going to sort by, you are going to end up with collisions in your sorting map, which will end up with values overwriting each other and ultimately values missing in your final iteration. This happens because say in my example I have two people with rank 1. First person comes through, their rank is 1, and their name is Sandy. So the sorting map has 1=sandy. Then another person comes through, they also have rank 1 and their name is jones. Now the map has 1=jones. Sandy just fell out of the list. How do you deal with this? The best hack-ish fix I could come up with is to see if the key you are attempting to write to already exists, if so, then write to a slightly higher value key. Basically replace
for(contestEntry entry : entries.values())
{
rankToNameMap.put(entry.rank,entry.name);
}
with
for(contestEntry entry : entries.values())
{
decimal rank = entry.rank;
while(rankToNameMap.containsKey(rank))
{
system.debug('------ INCRIMENTING Rank TOTAL FOR ' + entry.name);
rank += 0.001;
}
rankToNameMap.put(rank,entry.name);
}
This won’t affect the value displayed when you retrieve and display the object, it just changes the sort order. I haven’t tested this throughly though so don’t rely on it extensively.
Anyway, there you have it Apex fans. A method to reliable (if not quickly or efficiently) sort Apex maps by just about anything. Once you understand the concepts it’s fairly easy to expand to sort to your hearts desire.
Originally posted by Kenji776 @ http://iwritecrappycode.wordpress.com/2011/09/28/apex-sorting-a-map/
Mastering the Force.com IDE: Searching metadata
The Force.com IDE is a developer application that is great utility for Salesforce.com administrators even if they never have to write or troubleshoot Apex or Visualforce. The most useful capability for any admin is the Search feature which allows you to find all the places a field is used: workflow, reports, templates, formulas, etc.
Let’s consider this scenario: you’ve been hired to update a company’s Salesforce org and you discover a duplicate field on Accounts. You run reports and find out both fields are populated with data. Before you can decide which one to keep and which to delete, you need to know all the locations in which each field is used: reports, views, workflow, templates, etc. You can find all those configurations in minutes with your Salesforce org’s metadata in a project in the Force.com IDE.
I’ll assume you’ve already installed Eclipse and the Force.com IDE. If you haven’t, developer.force.com has a great page to help you; see you back here soon!
The first step is to create a Force.com project that contains all your metadata. Do this during the set up by selecting all the Metadata components.
If you have a very large amount of metadata and Eclipse throws a Java Out of Memory error, you can either apply this solution or choose fewer metadata components. You should at least include the following to ensure you are searching the common components that reference fields.
| Component | file extension |
| Apex Classes Visualforce Components Email Templates Page Layout Custom Objects Standard Objects Object Translations Visualforce Pages Profiles Reports(includes reports built from Custom Report Types) S-controls Apex Triggers Workflow |
.cls .component .layout .object .object .translation .page .profile .report .scontrols .trigger .workflow |
Once you have created your project, you’re ready to start your search!
- Click on your Project to highlight it
- Click on the Search menu in the menu bar and select Search… to open the Search dialog box
- Enter the API Name of the field you’re seeking in Containing Text
- Copy the following file extension names into the field called File Name Patterns:
*.cls, *.component, *.email, *.layout, *.object, *.page, *.profile, *.report, *.scontrols, *.trigger, *.workflow - Set the Scope option to “Selected Resource” to ensure that only one Project is being searched.
- Click Search
- The Search Results pane will open when your search is completed with a list of all locations where the field occurs.
The following video demonstrates these steps in the Force.com IDE for Windows (Mac version is similar)
The search results will show you a tree with all the results in each metadata component. Click on any result will open the metadata file and show you exactly where your field occurs. You’ll be viewing XML, but don’t worry if you’ve never looked at XML before, there’s nothing tricky here. In fact, the tags you see are very friendly. They’ll be “nested” which means indented like a outline so that you can tell which tags are subordinate to which other tags. With a little investigative analysis, you’ll be able to discern the exactly how a field is being used in the metadata.
For example, in this screenshot you can see the Search results pane showing the components in which the field was found. The Reports components are open and the report called “Server Alerts” has two matches. By clicking on the matches, the Server_Alerts.report XML file is opened and the matches are highlighted. In the XML you can see all the configurations for this particular report. The <columns> tags show which columns are displayed in the report’s output, so we can see that our “App Server” field is one of the output columns. We also see the field nested in the <filter> tags and can translate the XML tags to tell us that the field is being used in a filter with the operator “notEqual” and no value which means we are looking for records where App Server is not populated with any data. You can compare it to the actual report in the UI which is depicted in the second image below.


Salesforce Trigger when Rollups Summaries Not Possible
Master-Details relationships in Force.com are very handy but don’t fit every scenario. For instance, it’s not possible to implement a rollup summary on formula field or text fields. Here’s a small trigger that you can use for a starter for these types of situations. The code for each class is available at GitHub for your forking pleasure.
So here’s the (not very useful) use case. Sales Order is the Master object which can have multiple Sales Order Items (detail object). The Sales Order Item has a “primary” Boolean field and a “purchased country” field. Each time Sales Order Items are inserted or updated, if the Sales Order Item is marked as “primary” then the value of “purchased country” is written into the “primary country” field on the Sales Order. I’m assuming that there can only be one Sales Order Item per Sales Order that is marked as primary. Essentially this is just a quick reference on the Sales Order to see which country is primary on any of the multiple Sales Order Items. Not very useful but illustrative.
The code is broken down into a Trigger and an Apex “handler” class that implements the actual functionality. It’s best practice to only have one trigger for each object and to avoid complex logic in triggers. To simplify testing and resuse, triggers should delegate to Apex classes which contain the actual execution logic. See Mike Leach’s excellent trigger template for more info.
SalesOrderItemTrigger (source on GitHub) - Implements trigger functionality for Sales Order Items. Delegates responsibility to SalesOrderItemTriggerHandler.
trigger SalesOrderItemTrigger on Sales_Order_Item__c (after insert, after update) {
SalesOrderItemTriggerHandler handler = new SalesOrderItemTriggerHandler();
if(Trigger.isInsert && Trigger.isAfter) {
handler.OnAfterInsert(Trigger.new);
} else if(Trigger.isUpdate && Trigger.isAfter) {
handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
}
}
SalesOrderItemTriggerHandler (source on GitHub) - Implements the functionality for the sales order item trigger after insert and after update. Looks at each sales order item and if it is marked as primary_item__c then moves the primary_country__c value from the sales order item to the associated sales order’s primary_country__c field.
public with sharing class SalesOrderItemTriggerHandler {
// update the primary country when new records are inserted from trigger
public void OnAfterInsert(List newRecords){
updatePrimaryCountry(newRecords);
}
// update the primary country when records are updated from trigger
public void OnAfterUpdate(List oldRecords,
List updatedRecords, Map oldMap,
Map newMap){
updatePrimaryCountry(updatedRecords);
}
// updates the sales order with the primary purchased country for the item
private void updatePrimaryCountry(List newRecords) {
// create a new map to hold the sales order id / country values
Map salesOrderCountryMap = new Map();
// if an item is marked as primary, add the purchased country
// to the map where the sales order id is the key
for (Sales_Order_Item__c soi : newRecords) {
if (soi.Primary_Item__c)
salesOrderCountryMap.put(soi.Sales_Order__c,soi.Purchased_Country__c);
}
// query for the sale orders in the context to update
List orders = [select id, Primary_Country__c from Sales_Order__c
where id IN :salesOrderCountryMap.keyset()];
// add the primary country to the sales order. find it in the map
// using the sales order's id as the key
for (Sales_Order__c so : orders)
so.Primary_Country__c = salesOrderCountryMap.get(so.id);
// commit the records
update orders;
}
}
Test_SalesOrderItemTriggerHandler (source on GitHub) - Test class for SalesOrderItemTrigger and SalesOrderItemTriggerHandler. Achieves 100% code coverage.
@isTest
private class Test_SalesOrderItemTriggerHandler {
private static Sales_Order__c so1;
private static Sales_Order__c so2;
// set up our data for each test method
static {
Contact c = new Contact(firstname='test',lastname='test',email='no@email.com');
insert c;
so1 = new Sales_Order__c(name='test1',Delivery_Name__c=c.id);
so2 = new Sales_Order__c(name='test2',Delivery_Name__c=c.id);
insert new List{so1,so2};
}
static testMethod void testNewRecords() {
Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
soi1.Sales_Order__c = so1.id;
soi1.Quantity__c = 1;
soi1.Description__c = 'test';
soi1.Purchased_Country__c = 'Germany';
Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
soi2.Sales_Order__c = so1.id;
soi2.Quantity__c = 1;
soi2.Description__c = 'test';
soi2.Purchased_Country__c = 'France';
soi2.Primary_Item__c = true;
Sales_Order_Item__c soi3 = new Sales_Order_Item__c();
soi3.Sales_Order__c = so2.id;
soi3.Quantity__c = 1;
soi3.Description__c = 'test';
soi3.Purchased_Country__c = 'Germany';
soi3.Primary_Item__c = true;
Sales_Order_Item__c soi4 = new Sales_Order_Item__c();
soi4.Sales_Order__c = so2.id;
soi4.Quantity__c = 1;
soi4.Description__c = 'test';
soi4.Purchased_Country__c = 'Germany';
Sales_Order_Item__c soi5 = new Sales_Order_Item__c();
soi5.Sales_Order__c = so2.id;
soi5.Quantity__c = 1;
soi5.Description__c = 'test';
soi5.Purchased_Country__c = 'Italy';
insert new List{soi1,soi2,soi3,soi4,soi5};
System.assertEquals(2,[select count() from Sales_Order_Item__c where Sales_Order__c = :so1.id]);
System.assertEquals(3,[select count() from Sales_Order_Item__c where Sales_Order__c = :so2.id]);
System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
System.assertEquals('Germany',[select primary_country__c from Sales_Order__c where id = :so2.id].primary_country__c);
}
static testMethod void testUpdatedRecords() {
Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
soi1.Sales_Order__c = so1.id;
soi1.Quantity__c = 1;
soi1.Description__c = 'test';
soi1.Purchased_Country__c = 'Germany';
Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
soi2.Sales_Order__c = so1.id;
soi2.Quantity__c = 1;
soi2.Description__c = 'test';
soi2.Purchased_Country__c = 'France';
soi2.Primary_Item__c = true;
insert new List{soi1,soi2};
// assert that the country = France
System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
List items = [select id, purchased_country__c from Sales_Order_Item__c
where Sales_Order__c = :so1.id and primary_item__c = true];
// change the primary country on the sales order item. should trigger update
items.get(0).purchased_country__c = 'Denmark';
update items;
// assert that the country was successfully changed to Denmark
System.assertEquals('Denmark',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
}
}
Video Tutorial – Developing with Apex REST Services
I’ve had this video queued up for a few days now as I was waiting for the Apex REST API webinar. I didn’t want to steal Sandeep Bhanot’s and Alex Toussaint’s thunder. Their webinar was very informative and packed full of code! It should be available here any day now for your viewing pleasure.
Over at CloudSpokes we’ve been running development challenges with Apex REST services for quite awhile now. To facilitate future challenges I put together a video showing everything (yes, everything) you need to know to get started building applications with Apex REST services. The tutorial below walks you through the entire process of building our Members service. It shows you how to structure your services, how to use the new REST classes, and how to use the Apigee Console for Salesforce.com to test your services. The Apigee console even handles the OAuth for you!! Why write your own Java or Rails client app to test your classes? I’ve even published the entire code for the Members service so you can use it as a template. You can find the code here at github.
So here’s how to get started:
- Sign up for a new developer org at developer.force.com. All new orgs are now enabled with the Apex REST services so you can get started right away without waiting for the feature to get enabled.
- Take a peek at the Force.com REST API site for tutorials, code snippets, webinars and documentation. You can even post questions to the message boards for quick answers to your problems.
- Watch this awesome video by our friends at Apigee entitled RESTful API Design: Teach a Dog to REST. We love the design principles.
- Watch the video above for a quick tutorial services and then check out the code.
- Use the Apigee Console for Salesforce.com as your client application and start writing some code!
Changing tab logo using styling
Here is a step by step guide on how to add custom logo to a VisualforceTab
The following post is an answer to a question that was posted on the discussion board.
Step 1:
Create a Visualforce Tab
Username >Setup >App Setup >Create >Tabs> VisualforceTabs >New

Select Tabstyle
Step 2:
Click on Create your own style

Step 3:
Select color and insert your custom image.

Final step:
Navigate to the tab and it should display the custom logo
Please note: you need the following tag within your visualforce page to display the section header
<apex:sectionHeader title=”Teach Me Salesforce” />



