Template Retrofit
Home: NPSF
Contents
|
Code Snippets
Fixes to Beta 3.3 Ajax
Here is the general process as described by Steve Andersen
- Change reference to beta toolkit
<script src="/soap/ajax/14.0/connection.js"></script>
- kill beta 3.3 login calls (this is at the very top)
- fix all dynabean references
- Sforce.Dynabean replace with sforce.SObject
- sforceClient.Query with sforce.connection.query
- sforceClient.Update with sforce.connection.update
- sforceClient.Create with sforce.connection.create
- sforceClient.Retrieve with sforce.connection.retrieve
- must now only work with an array of ids, rather than with a single id (wrap a single id in [])
- sforceClient.DescribeSObject replace with sforce.connection.describeSObjects
- Fix all uses of fields as booleans
- search for "success" change to getboolean result[0].success == "true" change to result[0].getBoolean("success")
- batch size is called a bit differently (rare usage)
- delete is called a bit differently
- get records is now an array call (.records needs to become .getArray("records"))
- field names are case sensitive in the newer AJAX toolkit
Household for Contact S-Control
From Drew Piston
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title></title>
<script src="/soap/ajax/13.0/connection.js"></script>
<script id="clientEventHandlersJS" language="javascript">
<!--
function initPage() {
sforce.connection.init("{!API.Session_ID}","{!API.Partner_Server_URL_70}")
setup();
}
//Use this function as the entry point for your DHTML and JAVASCRIPT
processing
function setup() {
//create household
var Household = new sforce.SObject("ONEN_Household__c");
var HouseholdArray = new Array(1);
Household.set("Name","{!Contact.FirstName}" + " " + "{!Contact.LastName}" + " Household");
Household.set("MailingStreet__c","{!Contact.MailingStreet}");
Household.set("MailingCity__c","{!Contact.MailingCity}");
Household.set("MailingState__c","{!Contact.MailingState}");
Household.set("MailingPostalCode__c","{!Contact.MailingPostalCode}");
Household.set("MailingCountry__c","{!Contact.MailingCountry}");
Household.set("Recognition_Name__c","{!Contact.FirstName}" + " " + "{!Contact.LastName}");
Household.set("Recognition_Name_Short__c","{!Contact.FirstName}");
HouseholdArray[0] = Household;
//Call create on the Household
var HouseholdSaveResult = sforce.connection.create(HouseholdArray);
//if it worked, relate the Contacts to the household
if (HouseholdSaveResult[0].getBoolean("success") ) {
//get the object that was returned
CreatedHousehold = HouseholdSaveResult[0];
//create a Contact object for updating
var Contact = new sforce.SObject("Contact");
var ContactArray = new Array(1);
//Set the Id
Contact.set("Id","{!Contact.Id}");
//Set the Household Id
Contact.set("ONEN_Household__c", CreatedHousehold.id);
ContactArray[0] = Contact;
//Call create on the Opportunity.
var ContactSaveResult = sforce.connection.update(ContactArray);
//if it worked, redirect to the Household
if (ContactSaveResult[0].getBoolean("success")) {
top.location.replace("/" + CreatedHousehold.id);
} else {
alert("Error updating Contact! " + ContactSaveResult[0].message);
}
} else {
alert("Error in creating household!");
}
}
//-->
</script>
</head>
<body onload="initPage()">
</body>
</html>
StatBoy (03Oct2008 revision)
I rewrote this independently before becoming aware of Drew's work presented above. So be aware this does NOT represent any effort for instance to improve on Drew's...
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Create Household S-Control</title>
<script language="javascript" src="/soap/ajax/13.0/connection.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
<!--
function initPage() {
//Creates a new household record based on characteristics of the current
// contact record, and links that record to the starting contact record
//Revision of S-Control provided with non-profit template in Spring 2008
// to reference production Ajax 13.0 toolkit rather than Beta 3.3
// toolkit. Work completed by L.Bednar, 03Oct2008.
//alert("running initPage function");
//Calculate vals for household fields
var nameval = "{!Contact.FirstName} {!Contact.LastName}"
+ " Household";
//alert("calculated household name: " + nameval);
var recogname = "{!Contact.FirstName}" + " " + "{!Contact.LastName}";
//alert("calculated recognition name: " + recogname);
//create household object
var household = new sforce.SObject("ONEN_Household__c");
//alert("past creation of household object");
household.Name = nameval;
//alert("household.Name: " + household.Name);
household.MailingCity__c = "{!Contact.MailingCity}";
household.MailingCountry__c = "{!Contact.MailingCountry}";
household.MailingPostalCode__c ="{!Contact.MailingPostalCode}";
household.MailingState__c = "{!Contact.MailingState}";
household.MailingStreet__c = "{!Contact.MailingStreet}";
household.Recognition_Name__c = recogname;
household.Recognition_Name_Short__c = "{!Contact.FirstName}";
//alert("finished assigning values to contact object");
//create db household rec using household object
var householdSaveResult = sforce.connection.create([household]);
//alert("past rec creation stmt");
//If db household rec created, link the starting Contact
if (householdSaveResult[0].getBoolean("success")) {
//alert("Household successfully created");
//get the object that was returned
var createdHousehold = householdSaveResult[0];
//alert("HouseholdId: " + createdHousehold.id);
//create a Contact object for updating
var contact = new sforce.SObject("Contact");
//alert("passed new contact obj creation");
contact.Id = "{!Contact.Id}"; //set contact id
//alert("Contact.Id merge field val: {!Contact.Id}");
//alert("contact id: " + contact.Id);
contact.ONEN_Household__c = createdHousehold.id; //set household id
//alert("household id: " + contact.ONEN_Household__c );
//Update db contact rec using created contact obj
var contactSaveResult = sforce.connection.update([contact]);
//Test for success of db contact update
if (contactSaveResult[0].getBoolean("success")){
top.location.replace("/" + createdHousehold.id);
} else {
alert("Error updating Contact! " +contactSaveResult[0].message);
}
} else {
alert("Error in creating household!");
}
}
//-->
</script>
</head>
<body onload="initPage()">
<div id="js_output">
</div>
</body>
</html>
From StatBoy (08Oct2008 revision)
This version represents my effort to improve my earlier revision somewhat.
I made these revisions:
- changed JavaScript variable naming to avoid close similarities to words with special meanings in SalesForce, like "Contact", "Household", etc.
- inserted "try... catch" statement blocks to provide superior diagnostic messages to the user in case of failures
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Create Household S-Control</title>
<script language="javascript" src="/soap/ajax/13.0/connection.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
<!--
function initPage() {
//Creates a new household record based on characteristincs of the current
// contact record, and links that record to the starting contact record
//Revision of S-Control provided with non-profit template in Spring 2008
// to reference production Ajax 13.0 toolkit rather than Beta 3.3
// toolkit. Work completed by L.Bednar, 08Oct2008.
//alert("running initPage function");
//Calculate vals for household fields
var nameval = "{!Contact.FirstName} {!Contact.LastName}"
+ " Household";
//alert("calculated household name: " + nameval);
var recogname = "{!Contact.FirstName}" + " " + "{!Contact.LastName}";
//alert("calculated recognition name: " + recogname);
//create household SObject
var sobjH = new sforce.SObject("ONEN_Household__c");
//alert("past creation of household object");
sobjH.Name = nameval;
//alert("sobjH.Name: " + sobjH.Name);
sobjH.MailingCity__c = "{!Contact.MailingCity}";
sobjH.MailingCountry__c = "{!Contact.MailingCountry}";
sobjH.MailingPostalCode__c ="{!Contact.MailingPostalCode}";
sobjH.MailingState__c = "{!Contact.MailingState}";
sobjH.MailingStreet__c = "{!Contact.MailingStreet}";
sobjH.Recognition_Name__c = recogname;
sobjH.Recognition_Name_Short__c = "{!Contact.FirstName}";
//alert("finished assigning values to contact object");
//create db household rec using household object
try {
var saveResultH = sforce.connection.create([sobjH]);
//alert("past rec creation stmt");
}
catch(error) {
alert("Error creating household record. (" + error.defaultcode + ") "
+ error.faultstring );
}
//If db household rec created, link the starting Contact
if (saveResultH[0].getBoolean("success")) {
//alert("Household successfully created");
//get the object that was returned
var createdHousehold = saveResultH[0];
//alert("HouseholdId: " + createdHousehold.id);
//create a Contact SObject for updating
var sobjC = new sforce.SObject("Contact");
//alert("passed new contact obj creation");
sobjC.Id = "{!Contact.Id}"; //set contact id
//alert("SobjC.Id merge field val: {!Contact.Id}");
//alert("sobjC id: " + sobjC.Id);
sobjC.ONEN_Household__c = createdHousehold.id; //set household id
//alert("household id: " + sobjC.ONEN_Household__c );
//Update db contact rec using created contact obj
try {
var saveResultC = sforce.connection.update([sobjC]);
}
catch(error) {
alert("Error updating contact record (" + error.faultcode + ") "
+ error.faultstring );
}
//Test for success of db contact update
if (saveResultC[0].getBoolean("success")){
top.location.replace("/" + createdHousehold.id);
}
}
}
//-->
</script>
</head>
<body onload="initPage()">
<div id="js_output">
</div>
</body>
</html>
"Create Renewal" S-control
From StatBoy (12Dec2008 revision)
NOTE: The 13Oct2008 revision posted here previously contained errors in date calculation, the current version applies corrections.
This is my upgrade of the S-control delivered with the non-profit template in spring 2008.
I believe I've made a couple changes to use an additional SF "donation/opportunity" field or two to store characteristics of membership. I find the extra fields useful in my own customizations. So be aware of that please...
I also:
- applied a new naming convention and a set of standard abbreviations for JavaScript variables, as I found myself getting confused about use of words like "Opportunity" and "Contact" which have special SalesForce meanings already.
- eliminated statements that defined variables that simply assigned variable values that were then passed unchanged into the newly created "opportunity" record
- introduced "try..catch" statements that feel to me a little bit more useful for troubleshooting diagnostics
- rewrote functions designed to check for custom fields used in the function, but not present in SalesForce. The original simply gave the user a list of all the fields that were checked and said "something is missing". The revision actually tells the user WHICH fields are missing.
- wrote Javascript functions to replace calls to Sforce.Util functions for adding months to a date and formating a date into a text string.
"Create Renewal" HTML Definition
Displayed as a button on a donation/opportunity layout use for "membership" record types. This S-control should create a new OPEN "membership" record representing the expected membership renewal with appropriate "close date" as determined by the "membership start date" and "membership duration" specified on the starting "membership" record.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Creating Renewal S-Control</title>
<script language="javascript" src="/soap/ajax/13.0/connection.js" type="text/javascript">
</script>
<script language="javascript" type="text/javascript">
<!--
{!INCLUDE($SControl.Don_Create_Renewal_From_Membership_Conv)}
//-->
</script>
</head>
<body onload="initPage()">
<div id="jsout1">
</div>
<div id="jsout2">
</div>
<div id="jsout3">
</div>
<div id="jsout4">
</div>
<div id="jsout5">
</div>
<div id="jsout6">
</div>
</body>
</html>
"Functions_For_Dates" Snippet
Designed to add months by placing date result in the same part of the month as the starting date when adding a number of months. For instance, 2/29/2000 plus 12 months is returned as 2/28/2001. I thought the resulting functions might be reusable in other S-controls, so separated them into a snippet.
NOTE: The 13Oct2008 version of the snippet posted here previously produced some errors in data calculations that are corrected in the current version
//*****************************************************************************
// functions_for_dates.js - JavaScript functions for calculating a new date
// derived from a specified date with a number of specified months added,
// and functions for formatting date representations of dates
// Copyright (C) 2008 Larry Francis Bednar
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// The author may be contacted at:
// 3226 NE 24th Ave, Portland, OR 97212-2442 USA
// lb@quimdas.com
// www.quimdas.com
//
// $Revision: 1.4 $, $Date: 2008/12/12 17:25:48 $
//****************************************************************************
//**************************************************************
function isLeapYr(year) {
//If the provided year is a leap year, returns "true"
// otherwise, returns "
var mult4 = (year % 4 == 0 );
var mult100 = ( year % 100 == 0 );
var mult400 = ( year % 400 == 0 );
var result = false;
if ( mult4 ) {
result = true;
if ( mult100 && mult400) {
result = true;
} else if (mult100 && !mult400) {
result = false;
}
}
return result;
}
//*************************************************************
function dayEquivInMonth(day,month,year) {
//If the provided value for "day" is larger than
// the number of months allowed in the stated
// month and year, returns the maximum day allowed.
//Otherwise, returns the provided "day" value
//Expects input values with following characteristics:
// month in {0,11}
// year
// day in {1,31}
//Converts provided values to integers
var dayClean = Math.floor(day);
var monthClean = Math.floor(month);
var yearClean = Math.floor(year);
//Array of days in month
var MONTH_LIMITS = [31,28,31,30,31,30,31,31,30,31,30,31];
dayResult = 0;
var limit = 0; //Define local variable
if ( dayClean >= 1 && dayClean <= 31 &&
monthClean >= 0 && monthClean <= 11) {
if (month == 2) {
//If month is February, check for leap yr
if ( isLeapYr(year) ) {
limit = 29;
} else {
limit = 28;
}
} else {
limit = MONTH_LIMITS[ month ];
}
dayResult = ( day > limit? limit: day );
}
return dayResult;
}
//**************************************************************
function datePartsAddMonths(day,month,year,monthsToAdd) {
var addMonths = Math.floor(monthsToAdd);
//Adds specified number of months to provided date
// and returns result as a Date object
//NOTE: This function considers the addition of
// a month to place the result at the end of
// the next month, regardless of the number of
// days in the months
//Examples:
// addMonths(31,1,2008,1)= 2/29/2008
// addMonths(31,1,2007,1)= 2/28/2007
// addMonths(28,2,2008,1)= 3/28/2008
var dateResult = new Date();
var dayClean = Math.floor(day);
var monthClean = Math.floor(month) - 1; //convert to (0,11) basis
var yearClean = Math.floor(year);
if ( monthsToAdd < 0 ) {
//If monthsToAdd=-1.5 should imply monthsToAddClean = -1
var monthsToAddClean = Math.ceil(monthsToAdd);
} else {
var monthsToAddClean = Math.floor(monthsToAdd);
}
var monthDiff = monthClean + monthsToAddClean;
var monthResult = monthDiff - 12 * Math.floor(monthDiff/12);
var yearsToAdd = Math.floor( monthDiff/12);
var yearResult = yearClean + yearsToAdd;
var dayResult = dayEquivInMonth( dayClean, monthResult, yearResult );
dateResult.setDate(dayResult)
dateResult.setMonth(monthResult); //SetMonth uses {0,11} values
dateResult.setFullYear(yearResult);
return dateResult;
}
//*************************************************************
function dateAddMonths(dateStart,months) {
//Accepts Date object and adds specified number of months,
// returns result as a Date object
var dateResult = datePartsAddMonths( dateStart.getDate(),
dateStart.getMonth()+1,
dateStart.getFullYear(),
months );
return dateResult;
}
//*************************************************************
function dateToTextMDY( dateIn ) {
//Accepts Date object, outputs string representing date in
// MM/DD/YYYY format
if (dateIn instanceof Date) {
var result = (dateIn.getMonth() + 1) + "/"
+ dateIn.getDate() + "/"
+ dateIn.getFullYear();
} else {
result = "?";
}
return result;
}
"Functions_For_Field_Checks" Snippet
I felt this rewite of the little function provided in the beta S-control would make code maintenance easier. I thought the resulting functions might be reusable in other S-controls, so separated them into a snippet.
//********************************************************************
function fieldPresent(tableName, fieldName) {
// checks for presence of field in specified SalesForce table
var output = "";
tableName = tableName.toLowerCase();
//alert("table: " + tableName + "\nfieldName: " + fieldName );
var tableDef = sforce.connection.describeSObjects([tableName]);
var flds = tableDef[0].fields;
//alert("flds.length: " + flds.length);
var result = false;
for ( var i = 0; i < flds.length; i++ ) {
var fld = flds[i];
output += " " + i + " " + fld.name + "\n"
if (fld.name == fieldName) {
result = true;
break;
}
}
//alert("table fields checked: \n" + output);
//alert("fieldPresent: " + result);
return result;
}
//****************************************************************
function missingFields(tableName, fieldNames) {
// Expects a single value in "tableName" indicating a
// SalesForce table, and an array of strings in
// "fieldNames" representing fields whose presence
// will be checked. Returns a string
// containing names of missing fields
//alert("missingFields() running");
//alert("fieldNames.length: " + fieldNames.length);
var result = "";
for ( var i = 0; i < fieldNames.length; i++) {
//alert("checking for field: " + fieldNames[i]);
if ( fieldPresent(tableName, fieldNames[i]) == false ) {
result += " " + fieldNames[i] + "\n";
}
}
//alert("missing fields:\n" + result );
return result;
}
"Don_Create_Renewal_From_Membership_Conv" Snippet
//*****************************************************************************
// donation_create_renewal_form_current_membership - (For use with SalesForce
// spring 2008 nonprofit template) When executed from a displayed
// "donation/opportunity" record of record type "membership", will create
// a new "donation/opportunity" record representing an expected "renewal"
// from the same "Contact" and "Organization/Account", and with same
// "Contact Roles" as the starting record
// Copyright (C) 2008 Larry Francis Bednar
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// The author may be contacted at:
// 3226 NE 24th Ave, Portland, OR 97212-2442 USA
// lb@quimdas.com
// www.quimdas.com
//
// $Revision: 1.4 $, $Date: 2008/12/12 18:28:43 $
//****************************************************************************
//NOTES
// This version of this S-control is a revision of a version created by
// Steve Anderson, One/NW. The Anderson version used the Ajax Toolkit beta
// V3.3. This version instead uses the V13.0 Ajax Toolkit. A number of
// additional changes (to commenting, variable naming conventions, etc)
// have also been made. But the basic logic attempts to follow the Anderson
// version.
//
//****************************************************************************
//Include library of JavaScript date functions
{!INCLUDE($SControl.Functions_For_Dates)}
//Include library of JavaScript functions for checking field presence
{!INCLUDE($SControl.Functions_For_Field_Checks)}
/****************************************************************
* Main function called on page load
*****************************************************************/
function initPage() {
//alert("initPage() running");
var startUpResult = createRenewal();
}
/********************************************************************
* function to create membership renewal record
********************************************************************/
function createRenewal() {
//alert("createRenewal() running");
/****************************************************************
* Constants may differ between salesforce instances
*****************************************************************/
//Modified from use of Ajax beta 3.3 toolkit to use of V13.0 toolkit
// L.Bednar, 09Oct2008
//Test whether all expected fields are present in db
var testFieldNames = ["Anonymous__c", "Fund__c", "Membership_Length__c",
"Membership_Start_Date__c", "Membership_Type__c"];
//alert("testFieldNames: " + testFieldNames.join());
var testTableName = "Opportunity";
var testFieldResult = missingFields(testTableName, testFieldNames);
// missingFields function returns a string listing any tested fields that
// are not in db, returns empty string if all fields are present
if ( !(testFieldResult == "") ) {
alert("The following custom fields are used by this " +
"S-control, but are not present in your database: \n\r" +
testFieldResult );
}
//alert("passed field check");
//Get vals from starting opportunity
var oppId = "{!Opportunity.Id}";
//alert("oppId: " + oppId);
var acctName = "{!Opportunity.Account}";
//alert("AccountName: " + acctName);
var defaultMbrDuration = 12; //default membership duration, in months
var defaultMbrType = "Renewal";
var stage = "Open"; //default stage for renewals associated with active memberships
// get current opportunity info
//************************************************************
// handling of membership_start_date__c and membership_length__c added
// L.Bednar 14Aug2008
var fieldList = "AccountId, Amount, Anonymous__c, " +
"CloseDate, Fund__c, LeadSource, " +
"Membership_Length__c, Membership_Start_Date__c, " +
"Membership_Type__c, " +
"Name, RecordTypeId, Type";
//var fieldList = "Amount, Name, Type";
//alert("fieldList: " + fieldList);
try {
var oppOld = sforce.connection.retrieve( fieldList, "Opportunity", [oppId])[0];
}
catch(error) {
alert("Error retrieving oppOld: (" + error.faultcode + ") " + error.faultstring );
}
//alert("passed opp retrieval");
//Test whether all required field values are present on starting record
var testFieldValResult = "";
//Any field tested for values should have an if block similar to the next one
if ( !(oppOld.Membership_Start_Date__c) ) {
testFieldValResult += "Membership_Start_Date__c";
}
//If tests for any required values indicated problems, print message for user
if ( !(testFieldValResult == "") ) {
alert("The following REQUIRED fields do NOT have values: \n\r" +
testFieldValResult );
}
//alert("passed value check");
// Membership duration must be retrieved before close and membership date calcs
// If no duration specified, set to default value
var oppMbrDuration = oppOld.Membership_Length__c;
if ( !(oppMbrDuration) ) {
var mbrDuration = defaultMbrDuration;
alert("No membership duration specified. Default value of " + defaultMbrDuration + " months used");
}
//alert("mbrDuration: " + mbrDuration);
// calculate new membership start date
var mbrStartDate = dateAddMonths( oppOld.getDate("Membership_Start_Date__c"), mbrDuration );
//alert("recalculated mbrStartDate:" + mbrStartDate);
// calculate new estimated close date
// expected close date will be the date on which the next paid membership period starts
// close date for current rec is inappropriate because pmt for membership may arrive
// earlier or later than the actual start date of the new membership period
var closeDate = mbrStartDate;
// getMonth returns month in (0,11) basis, convert to (1,12) basis for use in record name
var closeDateLabel = dateToTextMDY( mbrStartDate );
//alert("closeDateLabel: " + closeDateLabel );
// Query salesforce for the contact roles on the selected opportunity
//***********************
var rolesOldQryStmt = "SELECT ContactId, Role, IsPrimary " +
"FROM OpportunityContactRole " +
"WHERE OpportunityId = '" + oppId + "'"
var rolesOld = sforce.connection.query( rolesOldQryStmt );
//alert("passed rolesOld query");
// put the response record in an array
//var rolesOldArray = rolesOld.records;
var rolesOldArray = rolesOld.getArray("records");
//alert("rolesOldArray.length: " + rolesOldArray.length );
for( row = 0; row < rolesOldArray.length; row++ ) {
var thisRole = rolesOldArray[row];
//alert("one contact role row visited");
// set values from the 'primary' contact role
if (thisRole.getBoolean("IsPrimary")) {
var contactId = thisRole.ContactId;
var roleName = thisRole.Role;
}
}
// retrieve primary contact info
var primaryContact = sforce.connection.retrieve("FirstName, LastName","Contact", [contactId])[0];
// if the primary role is individual donor, set the naming prefix to the Contact name,
// otherwise to the linked Account
if (roleName == "Individual Donor") {
var namePrefix = primaryContact.FirstName + " " + primaryContact.LastName;
} else {
var namePrefix = acctName;
}
//alert("Opportunity Name Prefix: " + namePrefix);
// create object for new opportunity
//********************************************************
var oppNew = new sforce.SObject("Opportunity");
var oppNewArray = new Array(1);
oppNew.AccountId = oppOld.AccountId
oppNew.Amount = oppOld.Amount
oppNew.Anonymous__c = oppOld.Anonymous__c;
oppNew.CloseDate = closeDate;
oppNew.Fund__c = oppOld.Fund__c;
oppNew.LeadSource = oppOld.LeadSource;
//NOTE: Merge field used to obtain value here - ALSO extracted with query of starting
// opp rec. Might be wise to use just ONE technique within this function.
var recTypeTxt="{!Opportunity.RecordType}";
// set the matching percent - NOTE type "match" not used in NWEI as of 25Aug2008
// and this record type should anyway NOT be a starting point for this automation
if (recTypeTxt=="match") {
oppNew.Matching__c = 100;
}
//alert("Opp.RecordType merge val: " + recTypeTxt + "\n"
// + "oppOld.Type: " + oppOld.Type);
oppNew.Membership_Length__c = oppMbrDuration;
oppNew.Membership_Start_Date__c = mbrStartDate;
oppNew.Membership_Type__c = defaultMbrType;
var nameSuffix = "";
if ( recTypeTxt == "Membership" ) {
nameSuffix = 'Membership';
} else {
nameSuffix = "NonMembership"; //Should never arise
}
var newName = namePrefix + " " + nameSuffix + " - " + closeDateLabel;
oppNew.Name = newName;
//alert("oppNew.Name: " + newName);
oppNew.RecordTypeId = oppOld.RecordTypeId;
oppNew.StageName = stage;
oppNew.Type = oppOld.Type;
//alert("oppNew.Type: " + oppNew.Type );
// Store object in array
var oppNewArray = new Array();
oppNewArray[0] = oppNew;
//alert("oppNewArray.length: " + oppNewArray.length );
// create new opportunity record in db
try {
var oppSaveResult = sforce.connection.create(oppNewArray);
}
catch(error) {
alert("Error saving donation to db. (" + error.faultcode + ") "
+ error.faultstring );
}
//alert("passed new opp rec creation");
if ( oppSaveResult[0].getBoolean("success") ) {
//alert("oppSaveResult success detected");
// get the object that was returned
oppCreated = oppSaveResult[0];
// clone all the contact roles
var rolesSaveResult = setContactRoles(oppCreated.id, rolesOldArray);
if (rolesSaveResult[0].getBoolean("success")) {
// redirect to editing the newly created opportunity
top.location.replace("/" + oppCreated.id + "/e?retURL=%2F" + oppCreated.id);
}
}
return "completed";
}
/*******************************************************************
* function for cloning all the contact roles for the existing opp to the new opp
********************************************************************/
function setContactRoles( newOppId, rolesOldArray){
//alert("setContactRoles() running");
// create an array to pass it to update
var rolesNewArray = new Array();
for( var row = 0; row < rolesOldArray.length; row++ ) {
//alert("processing row " + row );
var thisRole = rolesOldArray[row];
// create the object for opportunity contact role
var role = new sforce.SObject("OpportunityContactRole");
// link to new opportunity
role.OpportunityId = newOppId;
// set values to match old contact rols
role.ContactId = thisRole.ContactId;
role.Role = thisRole.Role;
role.IsPrimary = thisRole.IsPrimary;
// place sobject in array
rolesNewArray[row] = role;
}
//alert("rolesNewArray.length: " + rolesNewArray.length );
try {
var saveResult = sforce.connection.create(rolesNewArray);
} catch(error) {
alert("Error saving contact roles (" + error.faultcode + ") "
+ error.faultstring );
}
return saveResult;
}
"Create_Class_For_Contact" S-Control
From StatBoy (Oct2008 revision)
This is NOT a standard S-control delivered with the spring 2008 nonprofit template. But I felt it might provide a useful example for some SalesForce customizations. This S-control is displayed on a "Contact" record within a related list for linked custom objects in a custom "Class__c" object designed to hold records characterizing classes held by the organization using the SalesForce instance. It creates a record in "Class__c" representing a class held at the Contact's address and links to the beginning Contact record to represent the role of the Contact as a class "host". I also created a version that performs similar function when starting from an organization/account record.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>"Create Class for Contact" S-Control</title>
<script language="javascript"
src="/soap/ajax/13.0/connection.js"
type="text/javascript"></script>
<script language="javascript"
type="text/javascript">
<!--
function initPage() {
//This function creates a new record for the custom SalesForce object
// "Class__c", filling in values that are obtained from the starting
// Contact record the user is located on
//Created as custom addition to NWEI implementation of SalesForce
// nonprofit template
//Completed L.Bednar, 07Oct2008
//alert("initPage() running");
//create class
try {
var sobjC = new sforce.SObject("Class__c");
}
catch(error) {
alert("Error creating SObject. (" + error.faultcode + ") "
+ error.faultstring );
}
//alert("past SObject creation");
var nameval = "{!Contact.FirstName}" + " " + "{!Contact.LastName}"
+ " ?? Class - MM/DD/YYYY";
sobjC.Name = nameval;
//alert("expected new class name: " + nameval);
//alert("passed name assignment");
sobjC.Loctn_Street__c = "{!Contact.MailingStreet}";
sobjC.Loctn_City__c = "{!Contact.MailingCity}";
sobjC.Loctn_State__c = "{!Contact.MailingState}";
sobjC.Host_Contact__c = "{!Contact.Id}";
//Call create on the Session
try {
var saveResultC = sforce.connection.create([sobjC]);
alert("created class record");
}
catch(error) {
alert("Error creating class session. (" + error.faultcode + ") "
+ error.faultstring );
}
//if it worked, move user to Class record for further input???
if ( saveResultC[0].getBoolean("success")) {
//alert("redirecting to class record");
//get the object that was returned
var createdRec = saveResultC[0];
//if it worked, redirect to the Class
top.location.replace("/" + createdRec.id);
}
}
//-->
</script>
</head>
<body onload="initPage()">
</body>
</html>
"Households_Copy_Address_between_Contact_and_Household" S-Control
From StatBoy (18Oct2008 revision)
This is my upgrade of the S-control delivered with the non-profit template in spring 2008.
I also:
- Changed all references to Ajax Toolkit beta 3.3 to use Ajax Toolkit V13.0
- Applied a new naming convention and a set of standard abbreviations for JavaScript variables, as I found myself getting confused about use of words like "Household" and "Contact" which have special SalesForce meanings already.
- Eliminated statements that defined variables that simply assigned variable values that were then passed unchanged into the newly created "opportunity" record
- Introduced "try..catch" statements that feel to me a little bit more useful for troubleshooting diagnostics
- Added a function to check for appropriate running conditions before running the main operation. For instance, if the user asks to copy a contact address to a household, the check function verifies that a linked household is present before proceeding. This.function also issues an explanatory message to the user if an innapropriate set of conditions is present.
- Attempted code S-control so it could be pretty easily used in a custom link (as in the spring 2008 template), OR as buttons (which I prefer). Only minor changes are needed in code to allow these alternate uses, and these changes are prominently commented.
- Introduced braces for statement blocks and semicolons terminating all statements in the small utility functions "Querystring" and "Querystring_get". While there was no particular problem with these functions, I felt that following the common recommendations to always use braces and semicolons would make the code easier to work with. Perhaps important for users who aren't expert JavaScript programmers.
NOTE: As of 18Oct2008, I am able to get this S-control to run correctly when invoked from detail page buttons, but receive an "INVALID_SESSION_ID" error when the same code is run using a custom link as provided with the spring 2008 template. It may take me several days to troubleshoot/resove this discrepancy.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!--
household_contact_addr_exchanger - S-Control designed for use with
SalesForce. Provides functions for exchanging addresses of
SalesForce "contact" and "household" records as implemented in
the SalesForce spring 2008 nonprofit template
Copyright (C) 2008 Larry Francis Bednar
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
The author may be contacted at:
3226 NE 24th Ave, Portland, OR 97212-2442 USA
lb@quimdas.com
www.quimdas.com
$Revision: 1.6 $, $Date: 2008/10/19 17:02:59 $
-->
<!--
Based on Household Address Updater for Salesforce.com
Copyright (C) 2005 Steve Andersen, steve@onenw.org,
ONE/Northwest, 1402 Third Ave, Suite 1000, Seattle, WA, 98101
Major revision by L.Bednar 16Oct2008
use Ajax Toolkit V13.0 rather than beta V3.3
many new comments to hopefully clarify code logic
new variable naming conventions
introduction of "try...catch" blocks for more useful
diagnostic messages to user
elimination of unused variables (small number)
reduction on use of merge fields to retrieve values
being transferred
elimination of some redundant duplicate processing
steps where not needed
-->
<html>
<head>
<title>Household Address Copier - AJAX Toolkit V13.0 Conversion</title>
<script src="/soap/ajax/13.0/connection.js"
type="text/javascript"></script>
<link href="/css/ie_global.css" rel="stylesheet" type="text/css">
<link href="/css/ie_navigation.css" rel="stylesheet" type="text/css">
<link href="/css/opportunities_styles.css" rel="stylesheet" type="text/css">
<script type="text/javascript">
/****************************************************************
* Main function called on page load
*****************************************************************/
function initPage() {
//alert("initPage() function running");
var result = checkConditions();
//alert("finished initPage and calls to other functions");
}
//*************************************************************************
function checkConditions() {
//Retrieves contact id, household id, copyDirection. If appropriate
// value combintation are encountered, runs the function to copy
// addresses. Otherwise, issues explanatory message to user and
// does nothing else
//alert("checkConditions() running");
//PARSE_URL specifies the method for determining transfer direction
//If true, parse page URL for "direction" parameter values of
// "toContact"
// "toHousehold"
// (suitable for use of control in custom links where this
// portion of URL has been specified)
//If false,
// presence of contact id value -> "toHousehold"
// absence of contact id value -> "toContact"
// (suitable for use of control as a button where the assumption
// is that the current record is being copied to RELATED records)
var PARSE_URL = false;
var conditionsOk; //boolean indicating whether starting conditions are OK
var startPage; //string indicating starting page
//Retrieve contact id merge value (if any) from current page
var conId = "{!Contact.Id}";
var hshldId ; //variable stores household id value
//merge field holding household id differs if
// current page is a contact or household record
if ( conId != "" ) {
//if merge field for contact id is encountered, starting page must be
// viewing a contact record
startPage = "contact";
hshldId = "{!Contact.ONEN_HouseholdId__c}";
} else {
startPage = "household";
hshldId = "{!ONEN_Household__c.Id}";
}
alert("conId: " + conId
+ "\nhshldId: " + hshldId
+ "\nstartingPage: " + startPage);
var copyDirection = ""; //Direction in which values will be moved
if ( PARSE_URL ) {
// Parse the query string for the type variable
//NOTE: This S-control expects that the value of "Direction" parameter has
// been set in the specification of the custom link URL. For instance,
// the string '&Direction="toContact"' might be located somewhere within
// that URL to specify the use of the S-control to copy address info from
// related household record to the current contact record
//NOTE: Seems that string length is excessive as 2d substr parameter in next stmt
var urlSubstr = parent.document.URL.substring(parent.document.URL.indexOf("?"),
parent.document.URL.length);
alert("urlSubstr: " + urlSubstr);
var qryString = new Querystring(urlSubstr);
copyDirection = qryString.get("direction", "toContact");
} else {
//Use presence absence of a "contact id" merge value to determine copy
// direction
alert("determining copy direction based on start page");
if ( conId != "" ) {
//presence of conId val implies start from contact rec
copyDirection = "toHousehold";
} else {
copyDirection = "toContact";
}
}
alert("copyDirection: " + copyDirection);
//Test for inappropriate conditions for exchanging addresses
var conditionsOk = true;
var msg = "";
if ( copyDirection == "toHousehold" && startPage == "household" ) {
//This condition COULD be acceptable if the S-control is modified to
// ASSUME just ONE of the related contact records as source of addresses
// to copy. However, that assumption may be problematic in cases where
// several contact recods are found. In 17Oct2008 version of this code,
// this is simply treated as an unacceptable condition for the automation
//IMPROVEMENT IDEA: query db for number of linked contact recs, if = 1
// then allow the function to proceed
conditionsOk = false;
msg = "copyDirection " + copyDirection
+ " must start from a 'contact' display, but the starting page was a '"
+ startPage + "' page";
} else if ( copyDirection == "toHousehold" && hshldId == "" ) {
conditionsOk = false;
msg = "copyDirection " + copyDirection
+ " REQUIRES a household id, but none was found";
} else if ( copyDirection == "toContact" && startPage == "contact" && hshldId == "" ) {
conditionsOk = false;
msg = "From '" + startPage + "', copyDirection " + copyDirection
+ " REQUIRES a household id, but none was found";
}
if ( conditionsOk ) {
//var result = copyAddress( conId, hshldId, copyDirection );
alert("checkConditions() 'conditionsOk' stmt block");
}
alert("checkConditions() completed");
return "completed checkConditions";
}
//**************************************************************************
function copyAddress( conId, hshldId, copyDirection ) {
alert("copyAddress() function running");
//if copy direction is from household to contact
if ( copyDirection == "toContact" ) {
alert("toContact stmt block");
//Retrieve values from household record used as value source
var qryTxtH = "SELECT MailingStreet__c, MailingCity__c, MailingState__c, "
+ "MailingPostalCode__c, MailingCountry__c "
+ "FROM ONEN_Household__c "
+ "WHERE Id='" + hshldId + "'";
var currH = sforce.connection.query(qryTxtH);
//put the response record in an array
var arCurrH = currH.getArray("records");
alert("arCurrH.length: " + arCurrH.length);
if ( arCurrH.length > 0 ) {
alert("household record retrieved");
//Define source household for transfer to contact(s)
// Same values will be used for either branch of next "if...else"
// stmt
var srcH = arCurrH[0];
//Create array for use in "update" call to database
var arC = new Array();
//Construct array of "contact" sobjects for use in "update" call
if ( conId != "" ) {
//contact id is only present from merge field if starting
// record was a contact record, implying transfer from
// related household to only the CURRENT contact record
alert("conId not empty stmt block");
var sobjC = new sforce.SObject("Contact");
sobjC.Id =conId;
sobjC.MailingStreet = srcH.MailingStreet__c;
sobjC.MailingCity = srcH.MailingCity__c;
sobjC.MailingState = srcH.MailingState__c;
sobjC.MailingPostalCode = srcH.MailingPostalCode__c;
sobjC.MailingCountry = srcH.MailingCountry__c;
//place the object into the "contacts" array
arC[0] = sobjC;
//Call create on the Opportunity.
} else {
//empty contact id implies that a household record was
// current when the S-control was started. Valuse
// from the household record will be copied to ALL
// related contact records
alert("conId empty stmt block");
var linkedContactsQryTxt = "SELECT Id "
+ "FROM Contact "
+ "WHERE ONEN_Household__c='" + hshldId + "'";
var qryHC = sforce.connection.query( linkedContactsQryTxt );
//put the query response record in an array
var arHC = qryHC.getArray("records");
for ( row=0; row < arHC.length; row++ ) {
currC = arHC[row];
var sobjC = new sforce.SObject("Contact");
sobjC.Id = currC.Id;
sobjC.MailingStreet = srcH.MailingStreet__c;
sobjC.MailingCity = srcH.MailingCity__c;
sobjC.MailingState = srcH.MailingState__c;
sobjC.MailingPostalCode = srcH.MailingPostalCode__c;
sobjC.MailingCountry = srcH.MailingCountry__c;
//place the object into the "contacts" array
arC[row] = sobjC;
}
}
alert("arC.length: " + arC.length);
//update the contacts - requires only a single call to "update"
// providing the array representing all contact recs to be
// modified
try {
var saveResultC = sforce.connection.update( arC );
} catch(error) {
alert("Error updating Contact(s) (" + error.faultcode + ") "
+ error.faultstring );
}
//Calculate overall save result for all contact recs updated
var saveResultAll = true;
var saveResultAny = false;
for ( i = 0; i < saveResultC.length; i++ ) {
alert("checking indiv save results");
if ( saveResultC[i].getBoolean("success") ) {
//change to true if even only one update success
saveResultAny = true;
} else {
//change to false if even only one update failure
saveResultAll = false;
}
}
//leave user on appropriate database record
if ( saveResultAny ) {
if ( saveResultAll == false ) {
alert("At least one of the contact(s) was NOT successfully updated");
}
if ( conId != "" ) {
//"toContact" from contact rec clearly implies new
// values on the starting contact record - place
// user there to allow easy confirmation of results
top.location.replace("/" + conId);
} else {
//"toContact" from household rec might update MULTIPLE
// related contact recs - leave user on starting
// household record to allow user to readily check
// results in ALL related contact records
top.location.replace("/" + hshldId);
}
}
} else {
//Setup of function indicates copying of vals to contact, but
// the fact that no rows were returned for query against household
// implies there is no related household record in database
alert("No household related to this Contact");
}
} else if ( copyDirection == "toHousehold" ) {
//IMPROVEMENT: Original beta toolkit version assigned values
// via contact merge fields. This can cause problems if the
// source street address value happens to contain more than one
// line. This version was rewritten to obtain values from
// the contact rec via a query to avoid that possible
// problem
alert("toHousehold stmt block");
var qryTxtC = "SELECT MailingStreet, MailingCity, MailingState, "
+ "MailingPostalCode, MailingCountry "
+ "FROM Contact "
+ "WHERE Id='" + conId + "'";
alert("qryTxtC: " + qryTxtC);
try {
var currC = sforce.connection.query(qryTxtC);
} catch(error) {
alert("Error retrieving Contact (" + error.faultcode + ") "
+ error.faultstring );
}
//put the response record in an array
arCurrC = currC.getArray("records");
alert("arCurrC.length: " + arCurrC.length);
if ( arCurrC.length > 0 ) {
alert("source contact record retrieved");
var sobjH = new sforce.SObject("ONEN_Household__c");
var srcC = arCurrC[0];
alert("hshldId: " + hshldId);
sobjH.Id = hshldId;
sobjH.MailingStreet__c = srcC.MailingStreet;
sobjH.MailingCity__c = srcC.MailingCity;
sobjH.MailingState__c = srcC.MailingState;
sobjH.MailingPostalCode__c = srcC.MailingPostalCode;
sobjH.MailingCountry__c = srcC.MailingCountry;
alert("sObjH assignments: "
+ "\nId: " + sobjH.Id
+ "\nMailingStreet__c: " + sobjH.MailingStreet__c
+ "\nMailingCity__c: " + sobjH.MailingCity__c);
//place object in an array
var arH = new Array(1);
arH[0] = sobjH;
//update the database household record
try {
var saveResultH = sforce.connection.update( arH );
alert("update stmt block for household");
} catch(error) {
alert("Error updating Household (" + error.faultcode + ") "
+ error.faultstring );
}
//place user on household record containing newly copied values
// to allow easy confirmation of results
if (saveResultH[0].getBoolean("success")) {
top.location.replace("/" + hshldId);
}
}
}
}
//*******************************************************************************
/* Client-side access to querystring name=value pairs
Version 1.2.3
22 Jun 2005
Adam Vandenberg
*/
function Querystring(qs) { // optionally pass a querystring to parse
//Minor revisions by L.Bednar, 16Oct2008: adding braces to "if...else"
// statement blocks, semicolons at end of each command, etc.
this.params = new Object();
this.get = Querystring_get;
if (qs == null) {
qs = location.search.substring(1,location.search.length);
}
if ( qs.length == 0 ) {
return;
}
// Turn <plus> back to <space>
// See: http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4.1
qs = qs.replace(/\+/g, ' ');
var args = qs.split('&') // parse out name/value pairs separated via &
// split out each name=value pair
for ( var i=0; i<args.length; i++ ) {
var value;
var pair = args[i].split('=');
var name = unescape(pair[0]);
if ( pair.length == 2 ) {
value = unescape(pair[1]);
} else {
value = name;
}
this.params[name] = value;
}
}
//******************************************************************************
function Querystring_get( key, default_ ) {
//Minor revisions by L.Bednar, 16Oct2008: adding braces to "if...else"
// statement blocks, semicolons at end of each command, etc.
// change UNDEFINED to NULL
if ( default_ == null ) {
default_ = null;
}
var value = this.params[key]
if ( value == null ) {
value = default_;
}
return value;
}
</script>
</head>
<body onLoad="initPage();">
<textarea id="address" style="display: none">{!Contact_MailingStreet}</textarea>
</body>
</html>
"DonationSetupPledgefromOrganization" S-Control
From StatBoy (12Dec2008 revision)
In the SalesForce instance I'm now working in, this S-control contained references to the Beta 3.3 Ajax Toolkit and also contained "name=value" pairs in the link that were incorrect for fields in the custom "pledge" object. As of 12Dec2008, this version is functioning correctly in the SalesForce instance I'm working in.
<script language="JavaScript">
function redirect() {
parent.frames.location.replace("/a00/e?retURL=%2F{!Account.Id}&CF00N40000001KE0r={!Account.Name}&Name={!Account.Name} Pledge - Started {!Today}&00N40000001KE1Y={!Today}")
}
redirect();
</script>
"Donation_Set_up_Pledge_from_Contact" S-Control
From StatBoy (12Dec2008 revision)
In the SalesForce instance I'm now working in, this S-control contained references to the Beta 3.3 Ajax Toolkit and also contained "name=value" pairs in the link that were incorrect for fields in the custom "recurring donation" object. As of 12Dec2008, this version is functioning correctly in the SalesForce instance I'm working in.
</script><script language="JavaScript">
function redirect() {
parent.frames.location.replace("/a00/e?retURL=%2F{!Contact.Id}&CF00N40000001KE1z={!Contact.Name}&CF00N40000001KE0r={!Account.Name}&Name={!Contact.Name} Pledge - Started {!Today}&00N40000001KE1Y={!Today}")
}
redirect();
</script>
"Donation_Setup_Recurring_from_Contact" S-Control
From StatBoy (12Dec2008 revision)
In the SalesForce instance I'm now working in, this S-control contained references to the Beta 3.3 Ajax Toolkit and also contained "name=value" pairs in the link that were incorrect for fields in the custom "pledge" object. As of 12Dec2008, this version is functioning correctly in the SalesForce instance I'm working in.
<script language="JavaScript">
function redirect() {
parent.frames.location.replace("/a03/e?retURL=%2F{!Account.Id}&CF00N40000001KE2F={!Contact.Name}&CF00N40000001KE1x={!Account.Name}&Name={!Contact.Name} Recurring Donation - Started {!Today}")
}
redirect();
</script>
"Lead Converter" S-Control
From Evan Callahan (16Oct2008 revision)
Evan posted this to the Google Grouup Nonprofit SalesForce Practioners on 16Oct2008 and specifically encouraged copying to this wiki. Copied to wiki by StatBoy, 12Dec2008. Evan claimed the S-control seemed to be working, but had not be extensively tested.
By the way, you'll probably want to fill in the constant for the Individual acct Id in your account:
//CONSTANT: ID for "Individual" account
var IndividualAcctId = "0015000000FnAKn";
//Name for "Individual" account if above account is blank or not found
var IndividualAcctName = "Individual";
<!--
Lead Conversion Utility for Salesforce.com
Copyright (C) 2005 Steve Andersen, st...@onenw.org, ONE/Northwest, 1402 Third Ave, Suite 1000, Seattle, WA, 98101
Modifications (c) 2008 Evan Callahan, ev...@npowerseattle.org
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
---------------------------------------------------------------------
Description:
This scontrol handles lead conversion and can also be a part of a process for importing online payments generated
at an outside service. The steps in the overall process are as follows:
* Importing Online Payments to Lead Objects
* Comma Separated Values (CSV) file of payments is dowloaded from online donation service (PayPal, DonateNow, AuctionPay, etc.)
* CSV is imported to Leads via Lead Import process at salesforce.com
* Converting Leads to Contacts, Accounts, and Opportunities
* Leads Conversion tab (this scontrol) lists all open Leads, showing payment info if exists
* User attempts to match each Lead to an existing Contact with same email address or same last name
* If match is found, Lead is merged with Contact, and a closed Opportunity is created if necessary
* If user found no match, Lead is converted to new Contact and a closed Opportunity is created for that converted Contact.
* User is presented with links to Contact and Opportunity
OptionalCustomizations:
* Leads
* Custom fields on Lead to capture online payment information. In this example "Donation_Close_Date__c" and "Donation_Amount__c" for close date and amount of Donation
* This S-Control (search for CONSTANT to find where code changes are necessary)
* Install this code, making necessary changes to constants and adding any additional fields you want
* Important: Enter the values for open and closed leads into the code below
Because of the manual import of online donations as Leads, this scontrol can work with online donations from any service that allows export to CSV.
Last Modified: 10/29/2008-->
<html>
<head>
<title>Lead Conversion</title>
<script src="/soap/ajax/14.0/connection.js" type="text/javascript"></script>
<link href="/dCSS/Theme2/default/common.css" type="text/css" media="handheld,print,projection,screen,tty,tv" rel="stylesheet" ><link href="/dCSS/Theme2/default/custom.css" type="text/css" media="handheld,print,projection,screen,tty,tv" rel="stylesheet" ><link href="/css/assistive.css" type="text/css" media="aural,braille,embossed" rel="stylesheet" >
<style>
th,td{
font-size: 12px !important;
}
</style>
<script language="javascript1.2" type="text/javascript">
//CONSTANT: ID for "Individual" account
var IndividualAcctId = "0015000000FnAKn";
//Name for "Individual" account if above account is blank or not found
var IndividualAcctName = "";
//CONSTANT: STATUS YOU WANT TO USE FOR CONVERTED LEADS
var ConvertedLeadStatus = "";
//SET TO TRUE TO CREATE OPPORTUNITIES
var OppCreationEnabled = true;
//CONSTANT - Suffix to tack on the name of a created Opportunity
var OpportunityNameSuffix = 'Donation';
//CONSTANT - Date formats for creating the opportunity
var DateFormat = "M/d/yyyy";
var OppDateFormat ="yyyy";
//CONSTANT - Opportunity Type
//var OpportunityType = 'Cash/Check';
//CONSTANT - OpportunityContactRole role name - role for the Contact on the Opportunity
var OpportunityContactRoleName = "Individual Donor";
//CONSTANT - Opportunity Fund - custom field
//var Fund = 'General';
//Global variables
var LeadList = new Array();
var LeadId = "";
var LeadEmail = "";
var LeadFirstName = "";
var LeadLastName = "";
var LeadAccount = "";
var LeadRow = 0;
var AcctID = "";
var AcctName = "";
var aHTML = "";
var cHTML = "";
var LeadArray = new Array();
var LeadsToDo = 0;
var LeadsDone = 0;
var DeleteErrors = 0;
var CurrentLead;
var CurrentLeadDonationCloseDate;
var CurrentLeadDonationAmount;
var CurrentLeadOppName;
var ThisContactId;
var existingContact;
var processing=true;
/****************************************************************
* Main function called on page load
*****************************************************************/
function initPage() {
//sforce.connection.sessionId = "{!$Api.Session_ID}";
sforce.connection.batchSize=100;
// get the name of the Individual account (if the ID exists)
if (IndividualAcctId.length > 0) {
var IdArray = new Array();
IdArray[0] = IndividualAcctId;
var result = sforce.connection.retrieve("Name", "Account", IdArray);
if (result.className == "Fault" || result.length == 0 || result[0] == null) {
IndividualAcctId = "";
IndividualAcctName = "";
} else {
IndividualAcctName = result[0].get("Name");
}
}
HouseholdingEnabled = false;
if (check_field("contact", "ONEN_Household__c")) {
HouseholdingEnabled = true;
}
//check to see if the two custom fields have been added to the lead object
if (check_field("lead", "Donation_Close_Date__c") && check_field("lead", "Donation_Amount__c")) {
//set flag for creating opportunities if the fields have been added
OppCreationEnabled = true;
//Go get the list of Leads
sforce.connection.query("Select Id, FirstName, LastName, Company, Email, Donation_Close_Date__c, Donation_Amount__c from Lead where IsConverted = false", {onSuccess : processLeads, onFailure : sfError});
} else {
//set flag for creating opportunities to false if they haven't created the fields
OppCreationEnabled = false;
//Go get the list of Leads
sforce.connection.query("Select Id, FirstName, LastName, Company, Email from Lead where IsConverted = false", {onSuccess : processLeads, onFailure : sfError});
}
}
function processLeads(qr) {
if (qr.className == "Fault") {
errMsg("Error: " + qr.faultstring);
return;
}
if (qr.size > 0) {
//add the batch of results to the array of results
LeadList = LeadList.concat(qr.getArray("records"));
document.getElementById("msg").innerHTML = "Querying leads... " + LeadList.length + " of " + qr.size;
if (qr.done == false) {
//get the next batch
sforce.connection.queryMore(qr.queryLocator, {onSuccess : processLeads, onFailure : sfError});
} else {
var ThisLead = LeadList[LeadRow];
LeadId = ThisLead.get("Id");
LeadFirstName = encode(ThisLead.get("FirstName"));
LeadLastName = encode(ThisLead.get("LastName"));
LeadEmail = ThisLead.get("Email");
LeadAccount = encode(ThisLead.get("Company"));
//Query salesforce for contacts that have the same email address.
//Jumps to processEmailMatch when done.
var leadQuery = "Select Id, FirstName, LastName, AccountId from Contact where Email='" + LeadEmail;
leadQuery += "'";
document.getElementById("msg").innerHTML = "Matching leads to existing contacts... ";
sforce.connection.query(leadQuery, {onSuccess : processEmailMatch, onFailure : sfError});
}
document.getElementById('leadtable').style.visibility='visible';
} else {
//if no lead records found
errMsg("No open leads found.");
}
}
function processEmailMatch(qr) {
//prepare to build the options list
cHTML = "";
aHTML = "";
//add any matched by email address to the list
if(qr.size>0) {
for (var row=0;row<qr.size;row++) {
ThisMergeCandidate = qr.getArray("records")[row];
cHTML = "<option value='" + ThisMergeCandidate.get("Id") + "'>Merge with: " + ThisMergeCandidate.get("FirstName") + " " + ThisMergeCandidate.get("LastName") + "</option>";
}
cHTML += "<option value=''>Create new: " + decode(LeadFirstName) + " " + decode(LeadLastName) + "</option>";
// grab the first entry, which is selected by default
ThisContactId=qr.getArray("records")[0].get("Id");
//move on to find accounts
sforce.connection.retrieve("AccountId","Contact",[ThisContactId],{onSuccess : processAM1, onFailure : sfError});
} else {
//no match by email, search by name and add to list
sforce.connection.query("Select Id, FirstName, LastName, AccountId from Contact where LastName='"+ decode(LeadLastName) +"'", {onSuccess : processCMatch, onFailure : sfError}); // AND FirstName Like '"+ decode(LeadFirstName).substr(0,1) +"%'"
}
}
function processCMatch(qr) {
var cList = new Array();
ThisContactId = "";
//add matched contacts to the list
if(qr.size>0) {
for (var row=0;row<qr.size;row++) {
ThisMergeCandidate = qr.getArray("records")[row];
var ThisFirstName = ThisMergeCandidate.get("FirstName");
var ThisId = ThisMergeCandidate.get("Id");
// HTML for the merge option
var optHTML = "<option value='" + ThisId + "'>Merge with: " + ThisFirstName + " " + ThisMergeCandidate.get("LastName") + "</option>";
// exact match gets added to beginning of list
if (ThisFirstName == LeadFirstName) {
ThisContactId = ThisId;
cList.unshift(optHTML);
} else {
cList.push(optHTML);
}
}
}
// HTML for the create new option
var optHTML = "<option value=''>Create new: " + decode(LeadFirstName) + " " + decode(LeadLastName) + "</option>";
if(ThisContactId=="") {
// no exact match, so new goes at the beginning
cList.unshift(optHTML);
// special case if no company is listed
if(LeadAccount=="" || LeadAccount=="[not provided]") {
if (IndividualAcctId=="") {
aHTML = "<option value=''>[not provided]</option>"
} else {
aHTML = "<option value='" + IndividualAcctId + "'>Merge with: " + IndividualAcctName + "</option>"
LeadAccount = IndividualAcctName;
}
for (var row=0;row<cList.length;row++) {
cHTML += cList[row];
}
AddRow();
return;
}
// look for matching account names
sforce.connection.query("Select Id, Name from Account where Name Like '" + decode(LeadAccount) + "%'", {onSuccess : processAM3, onFailure : sfError});
} else {
// first name was matched, so new goes at the end
cList.push(optHTML);
// select the account name
sforce.connection.retrieve("AccountId","Contact",[ThisContactId],{onSuccess : processAM1, onFailure : sfError});
}
// build the option list
for (var row=0;row<cList.length;row++) {
cHTML += cList[row];
}
}
function processAM1(qr) {
AcctId = qr[0].get("AccountId");
sforce.connection.retrieve("Name","Account",[AcctId],processAM2,true);
}
function processAM2(qr) {
AcctName = qr[0].get("Name");
//create the option value
aHTML += "<option value='" + AcctId + "'>Merge with: " + AcctName + "</option>";
AddRow();
}
function processAM3(qr) {
//add any matched by email address to the list
if(qr.size>0) {
for (var row=0;row<qr.size;row++) {
ThisAccountMergeCandidate = qr.getArray("records")[row];
aHTML += "<option value='" + ThisAccountMergeCandidate.get("Id") + "'>Merge with: " + ThisAccountMergeCandidate.get("Name") + "</option>";
}
}
aHTML += "<option value=''>New account: " + decode(LeadAccount) + "</option>";
AddRow();
}
function AddRow() {
var ThisLead = LeadList[LeadRow];
//Set even and odd parameters for row display
var oddRow = LeadRow % 2;
if (oddRow) {
var AlternatingRowClass = "odd";
} else {
var AlternatingRowClass = "even";
}
//set first and last parameters for row display
if (LeadRow == 0) {
var FirstLastRowClass = "first";
} else if(LeadRow == LeadList.length-1) {
var FirstLastRowClass = "last";
} else {
var FirstLastRowClass = "";
}
//Create a table row for each record
var LeadTable = document.getElementById('leadList');
var newRow = LeadTable.insertRow(-1);
newRow.setAttribute('class', " dataRow " + AlternatingRowClass + " " + FirstLastRowClass);
var MergeSelectName = "mcs" + LeadId;
var AccountMergeSelectName = "amcs" + LeadId;
insertTD(newRow, "<input type='checkbox' name='ids' value='" + LeadId + "' id='ids'>","ids"+LeadId,"dataCell");
insertTD(newRow, "<a href='/" + LeadId + "' target='_lead'>" + decode(LeadFirstName) + " " + decode(LeadLastName) + "</a>", "Row" + LeadId + "Lead", "dataCell")
insertTD(newRow, LeadEmail, null, "dataCell");
insertTD(newRow, "<select name='" + MergeSelectName + "' id='" + MergeSelectName + "' onchange=\"selectAccountMergeCandidates('" + LeadId + "','" + LeadAccount + "');\">" + cHTML + "</select>", "Row" + LeadId + "Contact", "dataCell");
insertTD(newRow, "<select name='" + AccountMergeSelectName + "' id='" + AccountMergeSelectName + "'>" + aHTML + "</select>", "Row" + LeadId + "Account", "dataCell");
//insertTD(newRow, "<input name='Submit" + LeadId + "' type='submit' value='Convert' onclick=\"createLeadToConvert('" + LeadId + "');\"></input> <input type='submit' name='deleteLead' value='Delete' onclick=\"deleteLead('" + LeadId + "');\" />", "Row" + LeadId + "ConvertButton", "dataCell");
//show the opportunity fields if enabled
if (OppCreationEnabled) {
//If the date doesn't exist, show nothing, otherwise format it nicely
if (ThisLead.getDate("Donation_Close_Date__c")!=null) {
var LeadDonationDate = FormatDate(ThisLead.getDate("Donation_Close_Date__c"),DateFormat);
} else {
var LeadDonationDate = " ";
}
//If the amount doesn't exist, show nothing, otherwise format it nicely
if(ThisLead.get("Donation_Amount__c")!=null) {
var LeadDonationAmount = "$ " + ThisLead.get("Donation_Amount__c");
document.getElementById('dh1').innerHTML = "Opportunity Date";
document.getElementById('dh2').innerHTML = "Opportunity Amount";
} else {
var LeadDonationAmount = " ";
}
//write the cells
insertTD(newRow, LeadDonationDate, "Row" + LeadId + "oppdate", "dataCell");
insertTD(newRow, LeadDonationAmount, "Row" + LeadId + "oppamount", "dataCell");
} else {
insertTD(newRow, " ", null, "dataCell");
insertTD(newRow, " ", null, "dataCell");
}
// add the row to the table
//newRow.innerHTML = NewRowHTML;
// gray select boxes that have only one option
var ThisSelect = document.getElementById(MergeSelectName);
if (ThisSelect.length==1) ThisSelect.disabled = true;
var ThisSelect = document.getElementById(AccountMergeSelectName);
if (ThisSelect.length==1) ThisSelect.disabled = true;
LeadRow++;
if (LeadRow < LeadList.length) {
ThisLead = LeadList[LeadRow];
LeadId = ThisLead.get("Id");
LeadFirstName = encode(ThisLead.get("FirstName"));
LeadLastName = encode(ThisLead.get("LastName"));
LeadEmail = ThisLead.get("Email");
LeadAccount = encode(ThisLead.get("Company"));
document.getElementById("msg").innerHTML = "Matching leads to existing contacts... " + LeadRow + " of " + LeadList.length;
sforce.connection.query("Select Id, FirstName, LastName, AccountId from Contact where Email='"+ LeadEmail +"'", {onSuccess : processEmailMatch, onFailure : sfError});
} else {
document.getElementById("msg").innerHTML = "Matching leads to existing contacts... Done.";
processing = false;
}
}
/****************************************************************
* function to search for possible duplicates and create a select list that can be used in the conversion process
*****************************************************************/
function selectAccountMergeCandidates(ThisLeadId,ThisLeadAccount) {
//get the selected Contact Id from the Contact drop down
var MergeCandidateSelect = "mcs" + ThisLeadId;
var AccountMergeSelect = "amcs" + ThisLeadId;
//create the Account drop down box
var AccountLinkHTML = "<select name='" + AccountMergeSelect + "' id='" + AccountMergeSelect + "'>";
ThisContactId=document.getElementById(MergeCandidateSelect).options[document.getElementById(MergeCandidateSelect).selectedIndex].value;
//if we're merging to a Contact
if(ThisContactId!="") {
//Get the AccoutId for the selected Contact.
var ThisAccountMergeCandidate = sforce.connection.retrieve("AccountId","Contact",[ThisContactId])[0];
//get the account name for the chose Account.
ThisChosenAccount = sforce.connection.retrieve("Name","Account",[ThisAccountMergeCandidate.get("AccountId")])[0];
//create the option value
AccountLinkHTML += "<option value='" + ThisAccountMergeCandidate.get("AccountId") + "'>Merge with: " + ThisChosenAccount.get("Name") + "</option>";
} else if(ThisLeadAccount=="" || ThisLeadAccount=="[not provided]") {
if (IndividualAcctId=="") {
AccountLinkHTML += "<option value=''>[none provided]</option>"
} else {
AccountLinkHTML += "<option value='" + IndividualAcctId + "'>Merge with: " + IndividualAcctName + "</option>"
}
} else {
//Querying salesforce for any accounts that have that name.
var AccountMergeCandidates = sforce.connection.query("Select Id, Name from Account where Name Like '" + decode(ThisLeadAccount) + "%'");
//add any matched by email address to the list
if(AccountMergeCandidates.size>0) {
for (var row=0;row<AccountMergeCandidates.size;row++) {
AccountMergeCandidatesArray = AccountMergeCandidates.getArray("records");
ThisAccountMergeCandidate = AccountMergeCandidatesArray[row];
AccountLinkHTML += "<option value='" + ThisAccountMergeCandidate.get("Id") + "'>Merge with: " + ThisAccountMergeCandidate.get("Name") + "</option>";
}
}
AccountLinkHTML += "<option value=''>New account: " + decode(ThisLeadAccount) + "</option>";
}
//write the HTML to the table cell Div
document.getElementById("Row" + ThisLeadId + "Account").innerHTML = AccountLinkHTML + "</select>";
var ThisSelect = document.getElementById(AccountMergeSelect);
if (ThisSelect.length==1) ThisSelect.disabled = true;
}
/****************************************************************
* function to convert lead
*****************************************************************/
function createLeadToConvert() {
if (LeadArray.length>0){
if (ConvertedLeadStatus==""){
var LeadStatus = sforce.connection.query("Select MasterLabel from LeadStatus where IsConverted="+ true);
if(LeadStatus.size>0) {
LeadStatusArray = LeadStatus.getArray("records");
//set the status to the first converted one found
ThisLeadStatus = LeadStatusArray[0];
ConvertedLeadStatus = ThisLeadStatus.get("MasterLabel");
}
}
//if opp creation is enabled
if (OppCreationEnabled) {
//create the field list
var LeadFields = "FirstName, LastName, Company, Donation_Close_Date__c, Donation_Amount__c";
} else {
//create the field list
var LeadFields = "FirstName, LastName, Company";
}
//retrieve the lead
CurrentLead = sforce.connection.retrieve(LeadFields,"Lead",new Array(LeadArray[0]))[0];
var CurrentLeadCompany = CurrentLead.get("Company");
//if opportunity creation is turned on, get those values
if (OppCreationEnabled) {
CurrentLeadDonationAmount = CurrentLead.get("Donation_Amount__c");
CurrentLeadDonationCloseDate = CurrentLead.getDate("Donation_Close_Date__c");
if (CurrentLeadDonationCloseDate==null || CurrentLeadDonationCloseDate=="") CurrentLeadDonationCloseDate = new Date();
}
//Get the selected Contact Id from drop down.
var ContactId = document.getElementById("mcs" + LeadArray[0]).options[document.getElementById("mcs" + LeadArray[0]).selectedIndex].value;
//Get the selected Account ID from drop down
var AccountId = document.getElementById("amcs" + LeadArray[0]).options[document.getElementById("amcs" + LeadArray[0]).selectedIndex].value;
//create a Leadconverts object
var LeadToConvert = new sforce.LeadConvert;
//set the converted status for the lead
LeadToConvert.convertedStatus = ConvertedLeadStatus;
//the Lead Id we want to convert
LeadToConvert.leadId = LeadArray[0];
//if we're merging to a contact, use the first and last name, otherwise, use the lead's name data
if(ContactId!="") {
existingContact=true;
LeadToConvert.contactId = ContactId;
var ThisCurrentContact = sforce.connection.retrieve("FirstName, LastName","Contact",[ContactId])[0];
firstname = ThisCurrentContact.get("FirstName");
lastname = ThisCurrentContact.get("LastName");
} else {
existingContact=false;
firstname = CurrentLead.get("FirstName");
lastname = CurrentLead.get("LastName");
// do not allow Dick & Jane, or Jack & Diane, or Bonnie & Clyde
if (firstname.indexOf('&')>=0 || firstname.indexOf(' and ')>=0 || firstname.indexOf('&')>=0 || firstname.indexOf(' and ')>=0) {
errMsg("Contact names should not contain '&' or 'and'. Instead, you should create a second contact in the household. To continue, click '" + firstname + " " + lastname + "', click Edit, and change the lead name to one person before converting. After converting, add the second contact to the household manually.");
return;
}
}
//If we've picked an account to merge to, use that id
if(AccountId!=""){
LeadToConvert.accountId = AccountId;
} else {
//If we haven't picked an account, go look for an Id matching Company Name
var AccountMergeCandidates = sforce.connection.query("Select Id, Name from Account where Name='"+ CurrentLeadCompany +"'");
//if we matched, grab the first one's id
if(AccountMergeCandidates.size>0) {
AccountMergeCandidatesArray = AccountMergeCandidates.getArray("records");
ThisMergeAccount = AccountMergeCandidatesArray[0];
LeadToConvert.accountId = ThisMergeAccount.get("Id");
}
//if no match was found, create a new account (handled automatically by the Lead Convert process)
}
//create the opportunity if there is an amount
LeadToConvert.doNotCreateOpportunity = true;
if (OppCreationEnabled && CurrentLeadDonationAmount>0) {
//tell lead conversion to create an opp
LeadToConvert.doNotCreateOpportunity = false;
//give the new Opportunity a name
var LeadDonationDate = FormatDate(CurrentLeadDonationCloseDate,OppDateFormat);
CurrentLeadOppName = firstname + " " + lastname + " " + LeadDonationDate + " " + OpportunityNameSuffix;
LeadToConvert.opportunityName = CurrentLeadOppName;
}
//Convert the lead
document.getElementById("msg").innerHTML = "Converting leads.... " + LeadsDone + " of " + LeadsToDo;
sforce.connection.convertLead([LeadToConvert], {onSuccess : processConvertedLead, onFailure : sfError});
}
}
function processConvertedLead(LeadConvertResult) {
//handle success or failure
if (LeadConvertResult.className == "Fault") {
errMsg("Error: " + LeadConvertResult.faultstring);
return;
}
if (LeadConvertResult[0].getBoolean("success") == true) {
LeadsDone += 1;
var ContactId = LeadConvertResult[0].contactId;
var AccountId = LeadConvertResult[0].accountId;
//Get the first object from the array of objects
ThisConversionResponse = LeadConvertResult[0];
var ContactFields = "firstname, lastname, HomePhone";
if (HouseholdingEnabled) {
ContactFields+=",ONEN_Household__c";
}
var ThisContact = sforce.connection.retrieve(ContactFields,"Contact",[ContactId])[0];
var ThisAccount = sforce.connection.retrieve("Name ","Account",[AccountId])[0];
//create the link for the created/merged Contact
var linkHTML = "<a href='/" + ContactId + "' target=_'new_contact'>" + ThisContact.get("FirstName") + " " + ThisContact.get("LastName") + "</a>";
//var ThisAccount = sforce.connection.retrieve("Name","Account",ThisConversionResponse.accountId)[0];
var accountlinkHTML ="<a href='/" + AccountId + "' target=_'new_contact'>" + ThisAccount.get("Name") + "</a>";
//fix the contact record
//this is a fix for limitations of lead conversion
if (existingContact==true) {
// fix fields on contact if they are different on the converted lead
var needToFixIt = false;
var Contact = new sforce.SObject('Contact');
var ContactArray = new Array(1);
var s = CurrentLead.get("Home_Phone__c");
if (s == null && ThisContact.get("HomePhone") != null) {
Contact.set("HomePhone",s);
needToFixIt = true;
}
//Update the Contact.
if (needToFixIt==true) {
Contact.set("Id",ContactId);
ContactArray[0] = Contact;
var ContactSaveResult = sforce.connection.update(ContactArray);
if (ContactSaveResult[0].getBoolean("success") == true) {
} else {
alert(ContactSaveResult.toString());
}
}
}
//if we created an opportunity, put in the link
if (OppCreationEnabled && ThisConversionResponse.opportunityId!="") {
//create the link for the Opportunity
oppdatelinkHTML ="<a href='/" + ThisConversionResponse.opportunityId + "' target=_'new_contact'>"+FormatDate(CurrentLeadDonationCloseDate,DateFormat)+"</a>";
oppamountlinkHTML ="<a href='/" + ThisConversionResponse.opportunityId + "' target=_'new_contact'>$"+CurrentLeadDonationAmount+"</a>";
}
//display a link to the contact
document.getElementById("Row" + LeadArray[0] + "Contact").innerHTML = linkHTML;
//kill the link to the lead as it's now converted
document.getElementById("Row" + LeadArray[0] + "Lead").innerHTML = "Converted";
//blank out the checkbox
document.getElementById("ids" + LeadArray[0] ).innerHTML = " ";
//display a link to the Account
document.getElementById("Row" + LeadArray[0] + "Account").innerHTML = accountlinkHTML;
//modify the opportunity if we created one
if (OppCreationEnabled && CurrentLeadDonationAmount>0) {
//display a link to the opp
document.getElementById("Row" + LeadArray[0] + "oppdate").innerHTML = oppdatelinkHTML;
//display a link to the opp
document.getElementById("Row" + LeadArray[0] + "oppamount").innerHTML = oppamountlinkHTML;
//Create a new Opportuniy object so we can update the Opportunity we just created
var oppty = new sforce.SObject("Opportunity");
var opptyArray = new Array(1);
//Get the Id for the new Opportunity
oppty.set("Id",ThisConversionResponse.opportunityId);
//Set the Type
//oppty.set("Type",OpportunityType);
//Set the fund
//oppty.set("Fund__c",Fund);
//Set the close date to donation date
oppty.set("CloseDate", CurrentLeadDonationCloseDate);
//Set the amount to donation amount.
oppty.set("Amount", CurrentLeadDonationAmount);
//Set the Opportunity stage to closed won
oppty.set("StageName","Closed Won");
//drop the object into an array
opptyArray[0] = oppty;
//Call Update on the Opportunity.
var OpportunitySaveResult = sforce.connection.update(opptyArray);
if (OpportunitySaveResult.className == "Fault") {
errMsg(OpportunitySaveResult.toString());
return;
}
if (OpportunitySaveResult[0].getBoolean("success") == true) {
//this section modifies the automatically created OpportunityContactRole for the new Opportunity and Contact to have a Role define above and the Is Primary checkmark
//Query for the OpportunityContactRole record of the newly converted lead and contact
var OpportunityContactRole = sforce.connection.query("Select Id from OpportunityContactRole where OpportunityId='"+ ThisConversionResponse.opportunityId +"' and ContactId='" + ContactId + "'");
if (OpportunityContactRole.size!=0) {
OpportunityContactRoleArray = OpportunityContactRole.getArray("records");
ThisOpportunityContactRole = OpportunityContactRoleArray[0];
//call the ModifyContactRole function
var OpportunityContactRoleSaveResult = ModifyContactRole(ThisOpportunityContactRole.get("Id"),OpportunityContactRoleName);
} else {
alert(OpportunityContactRoleSaveResult.toString());
}
} else {
alert(OpportunitySaveResult.toString());
}
}
//if you have set up the ONE/Northwest custom object for householding and we're not merging to an existing contact, create a household
if (HouseholdingEnabled) {
if (!ThisContact.get("ONEN_Household__c") || ThisContact.get("ONEN_Household__c")=="") {
var ThisContact = sforce.connection.retrieve("FirstName, LastName, MailingStreet, MailingCity, MailingState, MailingPostalCode, MailingCountry","Contact",[ContactId])[0];
var Household = new sforce.SObject('ONEN_Household__c');
var HouseholdArray = new Array(1);
var ThisContactFirstName = handlenull(ThisContact.get("FirstName"));
if (ThisContactFirstName!="") {
var RecognitionName = ThisContactFirstName + " " + ThisContact.get("LastName");
var RecognitionNameShort = ThisContactFirstName;
} else {
var RecognitionName = ThisContact.get("LastName");
var RecognitionNameShort = ThisContact.get("LastName");
}
var HouseholdName = RecognitionName + " Household";
Household.set("Name",HouseholdName);
Household.set("MailingStreet__c",ThisContact.get("MailingStreet"));
Household.set("MailingCity__c",ThisContact.get("MailingCity"));
Household.set("MailingState__c",ThisContact.get("MailingState"));
Household.set("MailingPostalCode__c",ThisContact.get("MailingPostalCode"));
Household.set("MailingCountry__c",ThisContact.get("MailingCountry"));
Household.set("Recognition_Name__c",RecognitionName);
Household.set("Recognition_Name_Short__c",RecognitionNameShort);
HouseholdArray[0] = Household;
//Call create on the Household.
var HouseholdSaveResult = sforce.connection.create(HouseholdArray);
if (HouseholdSaveResult.className == "Fault") {
errMsg(HouseholdSaveResult.toString());
return;
}
if (HouseholdSaveResult[0].getBoolean("success") == true) {
//get the object that was returned
CreatedHousehold = HouseholdSaveResult[0];
//update the Contact so it's related to the new Household
var Contact = new sforce.SObject('Contact');
var ContactArray = new Array(1);
//Set the Recordtype
Contact.set("Id",ContactId);
//relate the two
Contact.set("ONEN_Household__c", CreatedHousehold.id);
ContactArray[0] = Contact;
//Update the Contact.
var ContactSaveResult = sforce.connection.update(ContactArray);
if (ContactSaveResult[0].getBoolean("success") == true) {
} else {
alert(ContactSaveResult.toString());
}
} else {
alert("Error in creating household!");
}
}
}
LeadArray.shift();
if (LeadArray.length > 0) {
createLeadToConvert();
} else {
var doneMsg = "Converted " + LeadsDone + " lead";
if (LeadsDone > 1) doneMsg+= "s";
doneMsg+=".";
document.getElementById("msg").innerHTML = doneMsg;
document.getElementById("MasterBox").checked = false;
}
} else {
alert(LeadConvertResult[0].errors[0].message);
}
}
/****************************************************************
* function to create Opportunity Contact Roles for a known Opp, Contact, and Role Name
*****************************************************************/
function ModifyContactRole(ContactRoleId,OpportunityContactRoleName){
//create the object for opportunity contact role
var ContactRole = new sforce.SObject('OpportunityContactRole');
ContactRole.set("Id",ContactRoleId);
//set the contact role to the default
ContactRole.set("Role",OpportunityContactRoleName);
//Make it primary
ContactRole.set("IsPrimary",1);
//create an array to pass it to Update
var contactRoleArray = new Array(1);
contactRoleArray[0] = ContactRole;
//Update it
return sforce.connection.update(contactRoleArray);
}
/****************************************************************
* function to deal with ' that would blow up the page
*****************************************************************/
function encode(string) {
if (string==undefined) {
return '';
} else {
string = string.replace("\'","`");
return string;
}
}
/****************************************************************
* function to put the ' back in the strings when displaying or storing in the database
*****************************************************************/
function decode(string) {
if (string==undefined) {
return '';
} else {
string = string.replace("`","\'");
return string;
}
}
/****************************************************************
* function to turn nulls into empty strings
*****************************************************************/
function handlenull(string) {
if(string!=null) {
string = string;
} else {
string = "";
}
return string;
}
/****************************************************************
* function to add a TD element to a TR element
*****************************************************************/
function insertTD(row, html, id, cls) {
var newCell = row.insertCell(-1);
if (html) newCell.innerHTML = html;
if (id) newCell.setAttribute('id', id);
if (cls) newCell.setAttribute('class', cls);
return newCell;
}
/****************************************************************
* function to look for a field on a given object. Thanks to Ron Hess of sf.com!
*****************************************************************/
function check_field(table, name) { // checks for custom field exists
name = name.toLowerCase();
if( ! /__c/.test(name) ) {
name += '__c';
}
var def = sforce.connection.DescribeSObjects(table);
var ret = def.fieldMap.containsItem(name);
return ( ret );
}
/****************************************************************
* function to check or uncheck all the checkboxes in the form
*****************************************************************/
function SelectChecked(form, element_name, value) {
var i = 0; for (i = 0; i < form.elements.length; i++) {
if (form.elements[i].name == element_name) {
form.elements[i].checked = value;
}
}
}
/****************************************************************
* function to compile the list of checked Leads, and process them
*****************************************************************/
function IsChecked(form, element_name, value, action) {
//don't start until matching is done
if (processing) return;
LeadArray = new Array();
for (i = 0; i < form.elements.length; i++) {
if ((form.elements[i].name == element_name) && (form.elements[i].checked)) {
LeadArray.push(form.elements[i].value);
}
}
if (LeadArray.length > 0) {
LeadsToDo = LeadArray.length;
LeadsDone = 0;
var ConfirmMsg = "Are you sure you want to " + action.toLowerCase() + " "+ LeadsToDo + " lead";
if (LeadsToDo >1) {
ConfirmMsg+="s";
}
ConfirmMsg+="?";
var x = window.confirm(ConfirmMsg);
if (x) {
if (action=="Convert") {
createLeadToConvert();
} else {
deleteLeads();
}
}
} else {
alert("No lead records are checked.");
}
}
/****************************************************************
* function to delete an array of Lead Ids
*****************************************************************/
function deleteLeads() {
document.getElementById("msg").innerHTML = "Deleting leads.... 0 of " + LeadsToDo;
sforce.connection.deleteIds(Get100Leads(), {onSuccess : deleteCallback, onFailure : sfError});
}
function deleteCallback(LeadDeleteResponse) {
//loop through the responses
for (i = 0; i < LeadDeleteResponse.length; i++) {
if (LeadDeleteResponse[i].getBoolean("success") != true) {
DeleteErrors++;
}
var DeletedLead = LeadDeleteResponse[i].id;
document.getElementById("Row" + DeletedLead + "Contact").innerHTML = " ";
//kill the link to the lead as it's now converted
document.getElementById("Row" + DeletedLead + "Lead").innerHTML = "Deleted";
//display a link to the Account
document.getElementById("Row" + DeletedLead + "Account").innerHTML = " ";
document.getElementById("ids" + DeletedLead ).innerHTML = " ";
}
//are we done yet?
if (LeadArray.length!=0) {
//continue updating the next batch
document.getElementById("msg").innerHTML = "Deleting leads.... " + LeadsDone + " of " + LeadsToDo;
sforce.connection.deleteIds(Get100Leads(), {onSuccess : deleteCallback, onFailure : sfError});
} else {
if (DeleteErrors == 0) {
document.getElementById("msg").innerHTML = "Deleted " + LeadsDone + " leads.";
} else {
document.getElementById("msg").innerHTML = "Deleted " + (LeadsDone - DeleteErrors) + " leads. There were " + DeleteErrors + " errors.";
}
document.getElementById("MasterBox").checked = false;
}
}
function Get100Leads() {
var LeadBatch = new Array();
while (LeadArray.length!=0 && LeadBatch.length<100) {
//grab an account from one array to the other
LeadBatch.push(LeadArray.shift());
}
LeadsDone += LeadBatch.length;
return LeadBatch;
}
function errMsg(errmsg) {
document.getElementById("msg").innerHTML = errmsg;
}
/****************************************************************
* SHARED FUNCTIONS
****************************************************************/
function sfError(error) {
//console.log(error);
alert("Error: " + (error.faultstring!=null ? error.faultstring : error.toString()));
}
//function to look for a field on a given object
function check_field(table, field) {
if( ! /__c/.test(field) ) {
field += '__c';
}
var def = sforce.connection.describeSObject(table);
for (var i=0; i<def.fields.length; i++) {
if (def.fields[i].name == field) {
return true;
}
}
}
//date format function from old Sforce.Util
function FormatDate(date,format) {
format=format+"";
var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
var result="";
var i_format=0;
var c="";
var token="";
var y=date.getFullYear()+"";
var M=date.getMonth()+1;
var d=date.getDate();
var E=date.getDay();
var H=date.getHours();
var m=date.getMinutes();
var s=date.getSeconds();
var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
var value=new Object();
if (y.length < 4) {
y = ""+(y-0+1900);
}
value["y"] = ""+y;
value["yyyy"] = y;
value["yy"] = y.substring(2,4);
value["M"] = M;
value["MM"] = LZ(M);
value["MMM"] = MONTH_NAMES[M-1];
value["NNN"] = MONTH_NAMES[M+11];
value["d"] = d;
value["dd"] = LZ(d);
value["E"] = DAY_NAMES[E+7];
value["EE"] = DAY_NAMES[E];
value["H"] = H;
value["HH"] = LZ(H);
if (H==0){
value["h"]=12;
} else if (H>12){
value["h"] = H-12;
} else {
value["h"] = H;
}
value["hh"]=LZ(value["h"]);
if (H>11){
value["K"]=H-12;
} else {
value["K"]=H;
}
value["k"]=H+1;
value["KK"]=LZ(value["K"]);
value["kk"]=LZ(value["k"]);
if (H > 11) {
value["a"]="PM";
} else {
value["a"]="AM";
}
value["m"] = m;
value["mm"] = LZ(m);
value["s"]=s;
value["ss"]=LZ(s);
while (i_format < format.length) {
c=format.charAt(i_format);
token="";
while ((format.charAt(i_format)==c) && (i_format < format.length)) {
token += format.charAt(i_format++);
}
if (value[token] != null) {
result=result + value[token];
} else {
result=result + token;
}
}
return result;
};
function LZ(x) {return(x<0||x>9?"":"0")+x}
//function to round and display 2 decimal places
function r2(n) {
var ans = (Math.round(n * 100))/100 + "";
var dot = ans.indexOf(".",0);
if (dot == -1) {ans = ans + ".00"}
else if (dot == ans.length - 2) {ans = ans + "0"}
return ans;
}
function isDate(p_Expression){
return !isNaN(new Date(p_Expression)); // <<--- this needs checking
}
function dateAdd(p_Interval, p_Number, p_Date){
if(!isDate(p_Date)){return "invalid date: '" + p_Date + "'";}
if(isNaN(p_Number)){return "invalid number: '" + p_Number + "'";}
p_Number = new Number(p_Number);
var dt = new Date(p_Date);
switch(p_Interval.toLowerCase()){
case "yyyy": {// year
dt.setFullYear(dt.getFullYear() + p_Number);
break;
}
case "q": { // quarter
dt.setMonth(dt.getMonth() + (p_Number*3));
break;
}
case "m": { // month
dt.setMonth(dt.getMonth() + p_Number);
break;
}
case "y": // day of year
case "d": // day
case "w": { // weekday
dt.setDate(dt.getDate() + p_Number);
break;
}
case "ww": { // week of year
dt.setDate(dt.getDate() + (p_Number*7));
break;
}
case "h": { // hour
dt.setHours(dt.getHours() + p_Number);
break;
}
case "n": { // minute
dt.setMinutes(dt.getMinutes() + p_Number);
break;
}
case "s": { // second
dt.setSeconds(dt.getSeconds() + p_Number);
break;
}
case "ms": { // second
dt.setMilliseconds(dt.getMilliseconds() + p_Number);
break;
}
default: {
return "invalid interval: '" + p_Interval + "'";
}
}
return dt;
}
</script>
</head>
<body onLoad="initPage();" class="lead overviewPage">
<a name="skiplink"><img src="/s.gif" height='1' width='1' alt="Content Starts Here" class="skiplink"></a><div class="bPageTitle"><div class="ptBody secondaryPalette"><div class="content"><img src="/s.gif" alt="Lookup" class="pageTitleIcon">