Tuesday, July 21, 2009

Clicking AutoCompleteExtender scrollbar causes postback

Clicking the AutoCompleteExtender scrollbar causes postback when attached to a TextBox where AutoPostBack="true", below is a workaround to resolve issue.

Workaround is just to check whether focus is on extender popup. If focus is on extender do not sumit (postback) form.

See example below

<script language=javascript>
function checkFocusOnExtender()
{
//check if focus is on productcode extender
if ( $find('aceProductCode')._flyoutHasFocus )
return false;
}
</script>


<form id="form1" runat="server" onsubmit="return checkFocusOnExtender();">

<asp:TextBox ID="txtProductCode" runat="server" CssClass="textbox" AutoCompleteType="disabled"
AutoPostBack="true" OnTextChanged="txtProductCode_TextChanged"></asp:TextBox>
<ajaxToolkit:AutoCompleteExtender ID="aceProductCode" runat="server" TargetControlID="txtProductCode"
UseContextKey="true" ServiceMethod="GetProductCode" CompletionInterval="10" EnableCaching="true"
CompletionSetCount="100" MinimumPrefixLength="1" DelimiterCharacters=";, :" CompletionListCssClass="autocomplete_completionListElement"
CompletionListItemCssClass="autocomplete_listItem" CompletionListHighlightedItemCssClass="autocomplete_highlightedListItem"">
</ajaxToolkit:AutoCompleteExtender>

</form>



Monday, July 13, 2009

Override Price - Opportunity Product MSCRM 4.0

During implanting Microsoft CRM 4.0 I came across an issue to override price on opportunity product. Out of box CRM4.0 doesn’t allow to override price on opportunity product. I tried couple of options like setting ispriceoverridden=true using javascript, a server side plugin to change price but no luck. Then a workaround worked which I would like to share with all of you. Workaround is a trick to use outbox box “Manual Discount” field (only allowed field where user can write something) to implement override price.


Out of box calculation of ExtendedAmount is
ExtendedAmount = (PricePerUnit * Qty) ManualDiscount

Example (Out of Box calculation):-
Product: XYZ
Price (Pricelist): $15
Qty: 10
Manual Discount: $10
Extended Amount: $140 = (($15 * 10) - $10)

In above example you cannot override price as calculation is done on server. Any changes in Price here will be overridden with pricelist on server.

Below are steps to allow price override opportunity product.

a) Add custom fields on Opportunity Product

a. New_priceoverridden (bit)– custom field to know is price overriddden

b. New_priceperunit (money) – custom field to save priceperunit

c. New_manualdiscount (money) – custom field to save manual discount.

d. New_amount (money)– custom field to save amount

b) Add above custom fields on form and move out of box fields to hidden tab

c) Add finally a database trigger which does a trick

/****** Object: Trigger [dbo].[UpdateOpportunityProductPrice]

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[UpdateOpportunityProductPrice]
ON [dbo].[OpportunityProductExtensionBase]
AFTER INSERT, UPDATE
AS

DECLARE @opportunityProductId uniqueidentifier

SELECT @opportunityProductId = i.OpportunityProductId FROM inserted i;

IF @opportunityProductId IS NOT NULL
BEGIN


DECLARE @new_priceoverridden bit
DECLARE @new_manualdiscount money
DECLARE @new_priceperunit money
DECLARE @new_amount money

SELECT @new_priceoverridden = i.new_priceoverridden,
@new_manualdiscount = i.new_manualdiscount,
@new_priceperunit = i.new_priceperunit,
@new_amount = i.qty * i.priceperunit
FROM inserted i

IF @new_priceoverridden = 0
BEGIN


UPDATE dbo.OpportunityProductBase
SET new_amount = @new_amount, manualdiscountamount = @new_manualdiscount,
extendedamount = isnull(baseamount,0) - isnull(manualdiscountamount,0)
WHERE opportunityProductId = @opportunityProductId


END
ELSE
BEGIN


UPDATE dbo.OpportunityProductBase
SET new_amount = @new_amount, manualdiscountamount = (isnull(baseamount,0) - isnull(@new_amount,0)) + isnull(@new_manualdiscount,0),
extendedamount = isnull(@new_amount,0) - isnull(@new_manualdiscount,0)
WHERE opportunityProductId = @opportunityProductId


END
END

Verifying above example after implementing workaround

Example:-
Product: XYZ
Price Overridden (Custom field): Yes
Price (Custom Field): $25 (price in pricelist is $15)
Qty: 10
Manual Discount (Custom Field): $10

calculation takes place in database trigger
Manual Discount (out of box) = -$110 = $150 - (($25 * $10) + $10))
Extended Amount: $260 = (($15 * 10) - (-$110))

Product: XYZ
Price Overridden (Custom field): No
Price (Custom Field): $15 (price from pricelist)
Qty: 10
Manual Discount (Custom Field): $10

calculation takes place in database trigger
Manual Discount (out of box) = $10
Extended Amount: $140 = (($15 * 10) - $10)

d) Change Opportunity Product – Quote Product relationship mapping to map custom fields instead of outbox fields. Unofficial way to change relationship mapping as Mapping is not displayed in Opportunity Product – Quote Product relationship

a) Get EntityMapId from database

SELECT EntityMapId, TargetEntityName, SourceEntityName FROM EntityMap WHERE (TargetEntityName like '%quotedetail%') AND (SourceEntityName like '%opportunityproduct%');

b) Copy EntityMapId from above query result

http://crmurl/Tools/SystemCustomization/Relationships/Mappings/mappingList.aspx?mappingId=D3644E14-657B-DD11-9769-001E0B4882E2

Please make sure above mappingid will be EntityMapId from query result.


Saturday, March 7, 2009

Sorting with Objects on multiple fields

Introduction

This article demonstrates how to apply sorting on objects. It is useful when you need to apply sorting on objects e.g. Object Person(Name, Age) and you want to apply sorting on Person->Name, or you may wish to apply sorting on multiple fields i.e. Person->name DESC + person->age DESC.

It is very useful when you want to apply multiple column sorting on GridViews with ObjectDataSource.

Using the code

Using Reflection its very easy to sort objects. I had used Lists to sort objects and ObjectComparer class is inherited from IComparer

ObjectComparer supports Single as well as Multiple sorting.

Collapse
//
//
//Test.cs
//Created an array of Person object, for which i wish to apply sorting

Person[] personArray = new Person[] {
new Person("Ritesh", 26),
new Person("Arpan", 20),
new Person("Arpan", 23),
new Person("Hiren", 22),
new Person("Ankit", 22),
new Person("Dhaval", 23),
new Person("Gaurav", 25)
};

//Sort array on field Name in Ascending Order
Array.Sort(personArray, new ObjectComparer<Person>("Name"));

//Sort array on field Name Decending and Age Ascending
Array.Sort(personArray, new ObjectComparer<Person>("Name DESC, Age ASC",true));

ObjectComparer.cs

[Serializable]
public class ObjectComparer<ComparableObject> : IComparer<ComparableObject>
{
#region Constructor
public ObjectComparer()
{
}

public ObjectComparer(string p_propertyName)
{
//We must have a property name for this comparer to work
this.PropertyName = p_propertyName;
}

public ObjectComparer(string p_propertyName, bool p_MultiColumn)
{
//We must have a property name for this comparer to work
this.PropertyName = p_propertyName;
this.MultiColumn = p_MultiColumn;
}
#endregion

#region Property
private bool _MultiColumn;
public bool MultiColumn
{
get { return _MultiColumn; }
set { _MultiColumn = value; }
}

private string _propertyName;
public string PropertyName
{
get { return _propertyName; }
set { _propertyName = value; }
}
#endregion

#region IComparer<ComparableObject> Members
/// <summary>
/// This comparer is used to sort the generic comparer
/// The constructor sets the PropertyName that is used
/// by reflection to access that property in the object to
/// object compare.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int Compare(ComparableObject x, ComparableObject y)
{
Type t = x.GetType();
if (_MultiColumn) // Multi Column Sorting
{
string[] sortExpressions = _propertyName.Trim().Split(',');
for (int i = 0; i < sortExpressions.Length; i++)
{
string fieldName, direction = "ASC";
if (sortExpressions[i].Trim().EndsWith(" DESC"))
{fieldName = sortExpressions[i].Replace(" DESC", "").Trim();
direction = "DESC";
}
else
{
fieldName = sortExpressions[i].Replace(" ASC", "").Trim();
}

//Get property by name
PropertyInfo val = t.GetProperty(fieldName);
if (val != null)
{
//Compare values, using IComparable interface of the property's type
int iResult = Comparer.DefaultInvariant.Compare(val.GetValue(x, null), val.GetValue(y, null));
if (iResult != 0)
{
//Return if not equal
if (direction == "DESC")
{
//Invert order
return -iResult;
}
else
{
return iResult;
}
}
}
else
{
throw new Exception(fieldName + " is not a valid property to sort on. It doesn't exist in the Class.");
}
}
//Objects have the same sort order
return 0;
}
else
{
PropertyInfo val = t.GetProperty(this.PropertyName);
if (val != null)
{
return Comparer.DefaultInvariant.Compare(val.GetValue(x, null), val.GetValue(y, null));
}
else
{
throw new Exception(this.PropertyName + " is not a valid property to sort on. It doesn't exist in the Class.");
}
}
}
#endregion

}

//

Wednesday, February 11, 2009

Bind Nested Tree view from database - C#








Introduction

This is an example of how to bind a nested tree from database.

Very Simple algorithm to bind tree.

method insertnodes is called recursively which will bind a nested tree at n level

create one table in access with following fields

module_id integer

module_name varchar(50)

parent_module integer

place one treeview control, and call this method with first parameter null

insertnodes(null,0);

private void insertnodes ( TreeNode n , int module_id )

{

OleDbConnection con=new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\tree.mdb");

con.Open();

OleDbCommand cmd=new OleDbCommand( "SELECT * FROM [module] WHERE parent_module=" + module_id,con);

OleDbDataReader rdr=cmd.ExecuteReader();

while(rdr.Read())

{

TreeNode t=new TreeNode(rdr["module_name"].ToString());

insertnodes(t,Convert.ToInt16(rdr["module_id"].ToString()));

if(n==null)

treeView1.Nodes.Add(t);

else

n.Nodes.Add(t);

}

rdr.Close();

}

Regards,
Ritesh

Monday, February 9, 2009

Sorting with Objects on multiple fields - C#

This article demonstrates how to apply sorting on objects. It is useful when you need to apply sorting on objects e.g. Object Person(Name, Age) and you want to apply sorting on Person->Name, or you may wish to apply sorting on multiple fields i.e. Person->name DESC + person->age DESC.

It is very useful when you want to apply multiple column sorting on GridViews with ObjectDataSource.


Using Reflection its very easy to sort objects. I had used Lists to sort objects and ObjectComparer class is inherited from IComparer

ObjectComparer supports Single as well as Multiple sorting.

//
//
//Test.cs
//Created an array of Person object, for which i wish to apply sorting

Person[] personArray = new Person[] {
new Person("Ritesh", 26),
new Person("Arpan", 20),
new Person("Arpan", 23),
new Person("Hiren", 22),
new Person("Ankit", 22),
new Person("Dhaval", 23),
new Person("Gaurav", 25)
};

//Sort array on field Name in Ascending Order
Array.Sort(personArray, new ObjectComparer<Person>("Name"));

//Sort array on field Name Decending and Age Ascending
Array.Sort(personArray, new ObjectComparer<Person>("Name DESC, Age ASC",true));

ObjectComparer.cs


[Serializable]
public class ObjectComparer<ComparableObject> : IComparer<ComparableObject>
{
#region Constructor
public ObjectComparer()
{
}

public ObjectComparer(string p_propertyName)
{
//We must have a property name for this comparer to work
this.PropertyName = p_propertyName;
}

public ObjectComparer(string p_propertyName, bool p_MultiColumn)
{
//We must have a property name for this comparer to work
this.PropertyName = p_propertyName;
this.MultiColumn = p_MultiColumn;
}
#endregion

#region Property
private bool _MultiColumn;
public bool MultiColumn
{
get { return _MultiColumn; }
set { _MultiColumn = value; }
}

private string _propertyName;
public string PropertyName
{
get { return _propertyName; }
set { _propertyName = value; }
}
#endregion


#region IComparer<ComparableObject> Members
/// <summary>
/// This comparer is used to sort the generic comparer
/// The constructor sets the PropertyName that is used
/// by reflection to access that property in the object to
/// object compare.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int Compare(ComparableObject x, ComparableObject y)
{
Type t = x.GetType();
if (_MultiColumn) // Multi Column Sorting
{
string[] sortExpressions = _propertyName.Trim().Split(',');
for (int i = 0; i < sortExpressions.Length; i++)
{
string fieldName, direction = "ASC";
if (sortExpressions[i].Trim().EndsWith(" DESC"))
{fieldName = sortExpressions[i].Replace(" DESC", "").Trim();
direction = "DESC";
}
else
{
fieldName = sortExpressions[i].Replace(" ASC", "").Trim();
}

//Get property by name
PropertyInfo val = t.GetProperty(fieldName);
if (val != null)
{
//Compare values, using IComparable interface of the property's type
int iResult = Comparer.DefaultInvariant.Compare(val.GetValue(x, null), val.GetValue(y, null));
if (iResult != 0)
{
//Return if not equal
if (direction == "DESC")
{
//Invert order
return -iResult;
}
else
{
return iResult;
}
}
}
else
{
throw new Exception(fieldName + " is not a valid property to sort on. It doesn't exist in the Class.");
}
}
//Objects have the same sort order
return 0;
}
else
{
PropertyInfo val = t.GetProperty(this.PropertyName);
if (val != null)
{
return Comparer.DefaultInvariant.Compare(val.GetValue(x, null), val.GetValue(y, null));
}
else
{
throw new Exception(this.PropertyName + " is not a valid property to sort on. It doesn't exist in the Class.");
}
}
}
#endregion

}