Showing posts with label Tips and Tricks. Show all posts
Showing posts with label Tips and Tricks. Show all posts

Friday, October 30, 2009

SQL Server: The instance name must be the same as computer name

Saturday, 11 October 2008
Posted by David Jennaway at 10:11

This is something I’ve posted about on newsgroups, but one of my colleagues encountered it recently, and I think it deserves a blog entry.
The CRM Environment Diagnostics Wizard may throw the error ‘The instance name must be the same as computer name’. The most common cause of this is if the SQL Server has been renamed after SQL Server was installed. The reason is that, at installation time, SQL Server stores the computer name in a system table, sysservers. This information is not updated when the computer is renamed, and the error from the CRM Environment Diagnostics Wizard indicates that the entry in sysservers does not match the current computer name.
You can diagnose and resolve this by using some SQL system stored procedures. One of them lists the data in sysservers, the other 2 allow you to modify the data to reflect the current machine name.
To check if this is the issue, use SQL Management Studio (or Query Analyzer for SQL 2000) to execute the following query:
sp_helpserver
This will return output like the following:
Name,network_name,status,id,collation_name,connect_timeout,query_timeout
ORGNAME,ORIGNAME,rpc,rpc out,use remote collation,0,null,0,0
If the value in the name column does not match the current computer name, then you have to use the following SQL stored procedures to fix the problem. Note that sp_helpserver normally returns one record, but can return more records if you have configured linked servers. If this is the case, it is the row with id=0 that matters.
To change the information you have to first remove the incorrect record, then add the correct one, with the following queries:
sp_dropserver ‘ORIGNAME’ -- where ORIGNAME is the name returned by sp_helpserver
sp_addserver ‘CURRENTNAME’, ‘LOCAL’ – where CURRENTNAME is the current computer name
If you use named instances, refer to them in the form SERVERNAME\INSTANCENAME. It may then be necessary to restart SQL Server after these changes, but I'm not sure of this. It can't harm though if you can.
There is a KB article about this here. This descibes a similar solution, but be warned of a couple of minor issues with the solution - it fails to specify that quotes are required around the parameters to sp_dropserver and sp_addserver, and I have a feeling (though can't provide concrete evidence) that running sp_helpserver is more reliable than select @@servername.

Renaming Active Directory and Redeploy CRM

20:26 3/31/2009, noreply@blogger.com (Darren Liu), Darren's CRM Blog

I came across an issue today that the client would like to rename their AD because of some business decisions. They are wondering how to move CRM under the new domain.  Well to move CRM from old domain to the new domain, here are the steps that you can follow:

  1. Backup the [Organization]_MSCRM database.
  2. Backup the custom reports if you have any.
  3. Uninstall CRM 4.0 from the CRM server.
  4. Remove the MSCRM_Config database.
  5. Reinstall CRM 4.0 and choose to setup a new Organization option during the install.
  6. Restore the existing [Organization]_MSCRM database.
  7. Logon to the CRM server and launch CRM Deployment Manager.
  8. Import existing organization and then follow the wizard to remap the users in the new AD.
  9. Verify that you can logon back to the CRM environment.

That’s it! If you run into any this issue in the future, now you can follow the steps above to redeploy CRM.

Example Of Deactivating An Entity

by Danny Varghese 03.06.09

One of the least published example of CRM code is deactivating an entity, probably because it's not used as often as creating, updating or retrieving entities. Below is an example I've used on several occasions to deactivate an entity:

   1:  



   2: public void DeactivateEntity(Guid entityId)



   3: {



   4: //variable initialization



   5: SetStateDynamicEntityRequest deactivateReq = new 



   6: SetStateDynamicEntityRequest();



   7: //deactivate the cloned assignment



   8: deactivateReq = new SetStateDynamicEntityRequest();



   9: deactivateReq.State = "Inactive";



  10: deactivateReq.Status = 2;



  11: deactivateReq.Entity = new Moniker();



  12: deactivateReq.Entity.Name = <entity name>



  13: deactivateReq.Entity.Id = entityId;



  14: //execute the deactivation request



  15: service.Execute(deactivateReq);



  16: }


Debugging CRM Plug-ins, Stored Procedures & Custom Workflow Activities

by Danny Varghese 02.24.09

Whether it's developing CRM plug-ins, custom workflow activities, or writing stored procedures against the CRM database, the most useful tool I've used is the Microsoft Visual Studio Debugger. The debugger allows developers to step through the code for the above mentioned scenarios and has saved me hours!

Microsoft has phenomenal documentation on how to setup remote debugging:

http://msdn.microsoft.com/en-us/library/bt727f1t.aspx

The biggest issues I've had trying to setup the debugger has always been with permissions. I would recommend paying especially close attention to this section. The next biggest issue I had was trying to attach the debugger to a running process: http://msdn.microsoft.com/en-us/library/c6wf8e4z.aspx

Once you've setup Visual Studio debugger, you can attach the following processes for the following CRM components:

  1. For plug-ins, attach the debugger to the w3wp.exe process.
  2. For custom workflow activities, attach the debugger to MSCRMAsyncService.exe
  3. For stored procedures, attach the debugger to sqlserver.exe

Once the debugger has been attached, you must set a breakpoint in the code (either the .NET or T-SQL code). After the breakpoint is set, to test, do the following:

  1. For plug-ins, login to CRM and execute actions that will trigger the plug-in, such as create/update/assign a record.
  2. For custom workflow activities, login to CRM and perform actions that will trigger the workflow. With this, being that it's an asynchronous service, you'll have to wait until that service runs, and then Visual Studio will let you step through the code.
  3. For the stored procedure, just execute the stored procedure in Visual Studio and it will go right to the breakpoint.

I hope this post will help CRM developers save time and effort. I use the debugger every time I develop now to test and it's saved me tremendous amount of maintenance time and effort after the code has been deployed. Happy debugging!

Friday, February 27, 2009

Creating a Birthday Contact List

Today we welcome our guest blogger CRM MVP Darren Liu from the Crowe Horwath company.

Have you ever been asked by someone to get a list of contacts having birthdays during a certain time period from CRM? If so what have you done to perform this task? Within the application, birthdays are tracked on Contact records as a single date (including year). This causes problems when searching for birthdays in a certain time period as the birth date is evaluated including the year. To illustrate, consider the following example:

· John Dole, 10/1/1980

· Adam Smith, 9/1/1970

· Mark Francis, 10/10/1960

Within CRM, searching for date is done by range. There is no easy way to identify from the above contacts all those having birthday in October as any range you choose will include the year. Wildcard functions on date fields are not a workable solution.

There are several solutions to this problem including JavaScript to parse birthday on the onChange event, a custom report or a plug-in. The desired functionality is to be able to search by birth month, birth day, and/or birth year, allowing the user to quickly identify all birthdays in a certain time period.

In this blog, I will show you how to use a pre plug-in to parse the birthday field into day, month and year. This way, the users will able to perform searches using Advanced Find. I have chosen the plug-in approach because it will help me parse the birthday field not only when the users update the birthday on the contact form but also when updating the birthday through the CRM web service for data imports and data integration.

Implement the pre plug-in

1. Create New Attributes

Create three new attribute on the Contact entity form in CRM. After creating the new attributes, publish the Contact customization.

Display Name

Schema Name

Type

Searchable

Values

Birth Month

new_birthmonth

Picklist

Yes

Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6, Jul = 7, Aug = 8, Sept = 9, Oct = 10, Nov = 11, Dec = 12

Birth Day

new_birthday

Int

Yes

Min Value = 1

Max Value = 31

Birth Year

new_birthyear

Int

Yes

Min Value = 1900

Max Value = 9999

clip_image002

2. Create pre plug-in using Visual Studio

Create a plug-in project name Crm.Plugin, copy and paste the following code to your Plug-in project.

using System;


using System.Collections.Generic;


using System.Text;


using Microsoft.Crm.Sdk;


using Microsoft.Crm.SdkTypeProxy;


 


namespace Crm.Plugin


{


  public class MonthDayYearContactPlugin : IPlugin


  {


    public void Execute(IPluginExecutionContext context)


    {


    DynamicEntity entity = null;


 


    if (context.InputParameters.Properties.Contains(ParameterName.Target) &&


        context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)


    {


        entity = (DynamicEntity)context.InputParameters[ParameterName.Target];


        if (entity.Name != EntityName.contact.ToString()) { return; }


    }


    else


    {


        return;


    }


 


    try


    {


        if (entity.Properties.Contains("birthdate"))


        {


            CrmDateTime _birthdate = (CrmDateTime)entity["birthdate"];


            if (_birthdate.IsNull)


            {


                entity["new_birthday"] = CrmNumber.Null;


                entity["new_birthmonth"] = Picklist.Null;


                entity["new_birthyear"] = CrmNumber.Null;


            }


            else


            {


                DateTime birthdayValue = _birthdate.UserTime; 


                entity["new_birthday"] = new CrmNumber(birthdayValue.Day);


                entity["new_birthmonth"] = new Picklist(birthdayValue.Month);


                entity["new_birthyear"] = new CrmNumber(birthdayValue.Year);


            }


        }


    }


    catch (Exception ex)


    {


        throw new InvalidPluginExecutionException("An error occurred in the Month, Day, Year Plug-in for Contact.", ex);


    }


    }


  }


}


 




3. Register the plug-in The last step is to register the plug-in. To register the plug-in, you may use the Plug-in Registration tool from the MSDN Code Gallery. After the assembly is uploaded, you need to associate the following steps to the plug-in:





 
























Message: Create


Primary Entity: contact


Filtering Attributes: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Parent Pipeline



Message: Update


Primary Entity: contact


Filtering Attribute: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Parent Pipeline



Message: Create


Primary Entity: contact


Filtering Attributes: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Child Pipeline



Message: Update


Primary Entity: contact


Filtering Attribute: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Child Pipeline





Summary








That’s all there is to it! The users will now be able to use Advanced Find to quickly identify their contacts birthday in a certain time period from now on. For the existing contacts previously stored in CRM, you will need to write a one-time SQL script to update the birthday fields in the MSCRM database in order for CRM to return the correct data back to the users. Hopefully this will help you on your next CRM project.



clip_image004



clip_image006



Cheers,



Darren Liu



Published Friday, February 27, 2009 9:26 AM by crmblog

Windows Live ID, a primer

WLID

Windows Live ID is the authentication tool that is part of the Windows Live Service. With it you get free email, blog page, photo galleries, document storage, and more. Social networking tools such as Groups are also available. Groups allow you to invite people to participate in discussions, document sharing, events and calendars, and more.

Microsoft Dynamics CRM Online uses the Windows Live ID ( WLID ) to authentication who the user is.  You can also integrate the Windows Services with your Microsoft Dynamics CRM application.

Integration with the Windows Live Service can enhance your CRM experience. Using the document storage service, Sky Drive, can allow you to share documents with other users and people not in your CRM system such as customers, business partners, and colleagues.

I’ve created a video that will introduce you to Windows Live ID and it’s services. The video also shows how you can use Windows Live Groups and Document Storage with Microsoft Dynamics CRM.

Windows Live ID Overview

How to integrate your Windows Service Storage - SkyDrive with CRM Online

Also, you'll want to watch the video by fellow technology specialist Kevin Williamson on how to add users to Microsoft Dynamics CRM Online.

Cheers,

Jon White

Integrating a Firewalled CRM with your Website

Philip Richardson of Business Software in the Cloud


A couple of days ago Catherine Eibner posted a short tutorial showing how to integrate MSCRM with your Website. This works great if the back end of your website can access your CRM server. Now this isn’t always the case … this is where .NET Services can help.

Scenario

Contoso has a website which is hosted by a 3rd Party provider (eg. Web Central, Rackspace etc etc). Contoso also has a regular on-premise CRM 4.0 server (which is not exposed to the internet). Contoso wants it’s website to be able to read/write data from the CRM server in real time.

To solve this problem we would use the Service Bus. This service allows us to connect multiple firewalled systems together. A great intro to the Service Bus can be found in this PDC keynote session by Don Box and Chris Anderson. I’d also recommend this session by John Shewchuk: A Lap Around the Azure Services Platform.

Download our SDK and Register for our Services (please note there may be some delays in providing invite codes as we are ‘throttling’ access to our services during this phase of the Community Technical Preview).

Thursday, February 19, 2009

Change Activity History to "All" instead of "Last 30 Days"

David Fronk Dynamic Methods Inc. 2/13/2009 09:13:00 AM


While there have been other bloggers out ther who have blogged about this topic (Stunnware, CustomerEffective, etc) this request is made so often and the answer isn't always the easiest to find...so my hope is that if more of us MSCRM bloggers blog about then it should be easier for everyone to find and use. This code snippet comes from Stunnware (here's the link).
Just take the code below and put it in your onLoad script and you should be all set.
Enjoy!

/************************************************************** 


* Change the default view of a view selection combo box


**************************************************************/


SetDefaultView = function(viewCombo, viewName, appGrid) {


/* If the view has already been set, we don't need to do it again. */


if (viewCombo.value != viewName) {


/* Set the new view */


viewCombo.value = viewName;


/* Call RefreshGridView to run the code in the DHTML control.


* Without this call, only the selection in the combo box changes,


* but not the content of the grid */


appGrid.RefreshGridView();


}


}


/**************************************************************


* Event handler. Called whenever the ready state of the


* areaActivityHistoryFrame changes.


**************************************************************/


areaActivityHistoryFrame_OnReadyStateChange = function() {


/* Waiting until the frame has finished loading */


if (this.readyState == "complete") {


/* This is the frame we're interested in */


var frame = document.frames("areaActivityHistoryFrame");


/* And this is the view combo box */


var viewCombo = frame.document.getElementById("actualend");


/* This is the AppGridFilterContainer control we need to refresh the view */


var appGrid = frame.document.getElementById("AppGridFilterContainer");


/* The view combo box uses a style sheet that references a HTML


* control. We have to wait until the htc file is loaded,


* otherwise the call to FireOnChange in the SetDefaultView


* method will fail. */


if (viewCombo.readyState == "complete") {


/* If the control already has finished loading, we can


* directly set the new view. */


SetDefaultView(viewCombo, "All", appGrid);


}


else {


/* Otherwise we have to register another event handler


* waiting until all of the include files used by the


* combo box are loaded as well. */


viewCombo.onreadystatechange = function() {


if (this.readyState == "complete") {


SetDefaultView(this, "All", appGrid);


}


}


}


}


}


/* Set a new onclick event for the History navigation element


* This is where we register the onreadystatechange event handler */


if (document.getElementById('navActivityHistory') != null) {


document.getElementById('navActivityHistory').onclick = function() {


loadArea('areaActivityHistory');


document.frames('areaActivityHistoryFrame').document.onreadystatechange = areaActivityHistoryFrame_OnReadyStateChange;


}


}






David Fronk


Dynamic Methods Inc.

Thursday, February 12, 2009

Client Side Scripting - Customer fields again

SUNNWARE 10.02.2009


received quite a few emails recently pointing out that the customer field sample doesn't work or doesn't always work, so I finally decided to try another approach and came up with a new implementation, which is more elegant and smaller than the old one and hopefully more reliable as well. As usual, this is an unsupported customization.

In the meantime I received a modified version of the original sample, which I post in this article as well. 

The OnLoad code

You need an account lookup and a contact lookup field on your form to run this code. Some properties of the account lookup are modified in such a way that CRM treats it as a real customer field. It's also important to set the default value when initializing the "customer" (account) field with a contact, to prevent unwanted warning messages ("Do you really want to navigate away from this ....") when closing the form.

 

// Change "sw_accountid" and "sw_contactid" to the field names you are using.


var accountLookup = crmForm.all.sw_accountid;


var contactLookup = crmForm.all.sw_contactid;


 


// Set the available lookup types to account and contact.


accountLookup.lookuptypes = "1,2";


accountLookup.lookuptypenames = "account:1,contact:2";


 


// Set the icons to use for the account and contact.


accountLookup.lookuptypeIcons = "/_imgs/ico_16_1.gif:/_imgs/ico_16_2.gif";


 


// If there is an existing value stored in the contact lookup, then pass it to the "customer" lookup.


if (contactLookup.DataValue != null) {


 


   // Set the default value of our customer field to the contact. If you don't do this, CRM assumes


   // that the customer was changed and will display a warning messaging when closing the form


   // without saving.   


   accountLookup.DefaultValue = contactLookup.DataValue;


   accountLookup.DataValue = contactLookup.DataValue;


 


   if (typeof (accountLookup.DataValue[0].data) != "undefined") {


      // For some reason, the data property of the DataValue is set to an empty string, while the


      // data property of the DefaultValue is undefined. The data property is part of the comparison


      // CRM does when looking for modified fields. In order for CRM to truly believe that our


      // customer field wasn't changed - though we did - this data property has to be set accordingly.


      accountLookup.DefaultValue[0].data = accountLookup.DataValue[0].data;


   }


}


 




 



The OnSave code



The OnSave code simply stores the selected customer in the account and contact lookups. When using an OnChange event in the sw_accountid field, make sure to differentiate if the event was fired due to the user selecting a record or the OnSave modifying it. A simple way to do it, is setting a global variable in the OnSave event and comparing that value in OnChange.





// Change "sw_accountid" and "sw_contactid" to the field names you are using.


var accountLookup = crmForm.all.sw_accountid;


var contactLookup = crmForm.all.sw_contactid;


 


// If there is no value selected in "customer" lookup, then clear the contact lookup.


if (accountLookup.DataValue == null) {


   contactLookup.DataValue = null;


}


 


// Otherwise check the lookup type and copy the selected value to the contact lookup if appropriate.


else {


   var customer = accountLookup.DataValue[0];


 


   // A type code of 1 represents an account.


   if (customer.type == "1") {


      // If it is an account, then clear the contact lookup.


      contactLookup.DataValue = null;


   }


 


   else {


      // A contact was selected, so copy the value to the contact lookup and clear the account lookup.


      contactLookup.DataValue = accountLookup.DataValue;


      accountLookup.DataValue = null;


   }


}




 



Modified version of the original sample



As reported by some others, Alejandro Cesetti (ale [dot] cesetti [at] gmail.com) had problems with the original code, and sent me an email explaining the problem and the fix:



Reproducing the bug



When you save the entity having the “custom” Customer attribute with an Account being selected everything is ok. Then, if you open the same instance of the entity, and edit it changing the value of the Customer from an Account to a Contact, after you save your changes, the value shown in the Customer field is still the Account value and, what is more, the account relationship in DB isn’t updated to null keeping the relationship to the account.



How did I fix it



What I did was to hide the Account field as the Contact field and add another field in the place where I want the Customer field to be shown. Then the ExchangeLookups function will exchange the new lookup field with the Customer and vice versa instead of the Account field.



OnLoad Code





/******************************************************************************


* Global variables required to setup the customer field. You have to replace


* the values of AccountFieldName and ContactFieldName when using attribute


* names different than the one used in this sample.


*****************************************************************************/


AccountFieldName = "hud_accountid";


ContactFieldName = "hud_contactid";


CustomerFieldName = "hud_customerid";


CreatedByName = "createdby";


AccountLookup = crmForm.all.item(AccountFieldName);


ContactLookup = crmForm.all.item(ContactFieldName);


CreatedByLookup = crmForm.all.item(CreatedByName);


CustomerLookup = null;


CreatedByInnerHTML = null;


AllFieldsAvailable = (AccountLookup != null) && (ContactLookup != null);


 


/******************************************************************************


* GetLookupFieldHtml builds the inner HTML of a lookup control. Used to


* dynamically create the customer lookup.


*****************************************************************************/


function GetLookupFieldHtml(name, tabIndex, lookupTypes, lookupTypeNames, lookupTypeIcons, reqLevel) {


 


   var html = "<table class=\"ms-crm-Lookup\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"table-layout:fixed;\">" +


        "<tr>" +


        "<td>" +


        "<div ime-mode=\"auto\" class=\"ms-crm-Lookup\" tabindex=\"" + (parseInt(tabIndex) + 1) + "\"></div>" +


        "<label class=\"ms-crm-Hidden-NoBehavior\" for=\"" + name + "_ledit\"></label>" +


        "<input class=\"ms-crm-Hidden-NoBehavior\" ime-mode=\"auto\" type=\"text\" tabindex=\"" + tabIndex + "\" id=\"" + name + "_ledit\" maxlength=\"1000\"/>" +


        "</td>" +


        "<td width=\"25\" class=\"Lookup_RenderButton_td\">" +


        "<img src=\"/_imgs/btn_off_lookup.gif\" id=\"" + name + "\" class=\"ms-crm-Lookup\" req=\"" + reqLevel + "\" style=\"ime-mode:auto\" lookuptypes=\"" + lookupTypes + "\" lookuptypenames=\"" + lookupTypeNames + "\" lookuptypeIcons=\"" + lookupTypeIcons + "\" lookupclass=\"BasicCustomer\" lookupbrowse=\"0\" lookupstyle=\"single\" defaulttype=\"0\" autoresolve=\"1\" showproperty=\"1\" resolveemailaddress=\"0\">" +


        "<a href=\"#\" onclick=\"previousSibling.click();\" tabindex=\"-1\"></a>" +


        "</td>" +


        "</tr>" +


        "</table>";


 


   return html;


}


 


/******************************************************************************


* A sample event handler. As the customer lookup does not exist on the form,


* you cannot add an OnChange event handler in the CRM client. This method is


* used as an alternative.


*****************************************************************************/


function Customer_OnChange() {


   alert("Customer changed");


}


 


/******************************************************************************


* Helper function. Retrieves a field by its name.


*****************************************************************************/


GetField = function(name) {


   return crmForm.all.item(name);


}


 


/******************************************************************************


* Helper function. Retrieves the id of the TD element hosting a lookup control.


*****************************************************************************/


GetLookupCellId = function(name) {


   return name + "_d";


}


 


/******************************************************************************


* Helper function. Retrieves the TD element hosting a lookup control.


*****************************************************************************/


GetLookupCell = function(name) {


   return GetField(GetLookupCellId(name));


}


 


/******************************************************************************


* ExchangeLookups replaces an existing lookup control with another one. Used


* to replace the account lookup with the customer lookup and vice versa.


*****************************************************************************/


ExchangeLookups = function(lookupToShowId, lookupToHideId, newLookupHtml) {


   var prevCell = GetLookupCell(lookupToHideId);


   var prevCellIndex = prevCell.cellIndex;


   var row = prevCell.parentNode;


 


   row.deleteCell(prevCellIndex);


 


   var newCell = row.insertCell(prevCellIndex);


   newCell.id = GetLookupCellId(lookupToShowId);


   newCell.innerHTML = newLookupHtml;


}


 


/******************************************************************************


* CreateCustomerField creates the customer lookup and replaces the account


* lookup on the form.


*****************************************************************************/


function CreateCustomerField() {


   var customerValue;


 


   if (AccountLookup.DataValue != null) {


      customerValue = AccountLookup.DataValue;


   }


   else {


      customerValue = ContactLookup.DataValue;


   }


 


   var accountLookupEditField = GetField(AccountFieldName + "_ledit");


   var tabIndex;


 


   //If the account field supports the auto-resolve feature, then the tab-index is stored on the text field


   //of the lookup control


   if (accountLookupEditField == null) {


      tabIndex = AccountLookup.getAttribute("tabIndex");


   }


 


   //otherwise we use the same tabIndex as in the 3.0 implementation.


   else {


      tabIndex = accountLookupEditField.getAttribute("tabIndex");


   }


 


   //Standard values for a customer field


   var lookupTypes = "1,2";


   var lookupTypeNames = "account:1,contact:2";


   var lookupTypeIcons = "/_imgs/ico_16_1.gif:/_imgs/ico_16_2.gif";


   var reqLevel = AccountLookup.getAttribute("req");


   var customerIdHtml = GetLookupFieldHtml(CustomerFieldName, tabIndex, lookupTypes, lookupTypeNames, lookupTypeIcons, reqLevel);


 


   //    ExchangeLookups(CustomerFieldName, AccountFieldName, customerIdHtml);


   ExchangeLookups(CustomerFieldName, CreatedByName, customerIdHtml);


 


   CustomerLookup = GetField(CustomerFieldName);


   CustomerLookup.DataValue = customerValue;


   //  Here we are able to attach events to the customer field 


   //    CustomerLookup.onchange = Customer_OnChange;


}


 


/******************************************************************************


* Main code: If the required fields are avaialable on the form, the account


* lookup is replaced with a customer lookup. The CreatedByInnerHTML is saved to


* restore the original state later (see OnSave).


*****************************************************************************/


if (AllFieldsAvailable) {


   CreatedByInnerHTML = GetLookupCell(CreatedByName).innerHTML;


 


   CreateCustomerField();


}




 



OnSave Code





/******************************************************************************


* If the required fields are available on the form, the CreatedBy


* lookup was replaced with a customer lookup in the OnLoad event. To properly


* save the values, we have to restore the original state, meaning that the


* customer lookup has to be removed and the CreatedBy lookup has to be added


* back.


*****************************************************************************/


if (AllFieldsAvailable) {


   var customer = CustomerLookup.DataValue;


 


   ExchangeLookups(CreatedByName, CustomerFieldName, CreatedByInnerHTML);


 


   if (customer == null) {


      AccountLookup.DataValue = null;


      ContactLookup.DataValue = null;


   }


 


   else if (customer[0].type == "1") {


      AccountLookup.DataValue = customer;


      ContactLookup.DataValue = null;


   }


 


   else {


      AccountLookup.DataValue = null;


      ContactLookup.DataValue = customer;


   }


}