Wednesday, December 5, 2012

SAP B1 Forms Session

Forms Session
One of the difficulties when dealing with SAP B1 is the “stateless” condition that object have between events fired on the UI.
If one creates a class that handles the events for a given form then just creating a simple static variable would solve the “stateless” problem, but when you wish to have more then one instance of the form then this approach does not work.
To work around this I came up with the class below, which uses the FormUID (the unique identifier of the form) to identify the object that belongs to it. Meaning that you create your object and store it by using the FormUID as the key.
When an event gets fired you simply ask the FormsSession object to return you the object stored for the FormUID. This takes care of both problems, the “stateless” condition and the single class object.
One thing not to forget is to remove the object from the Session when the form closes (or like I intent to do, improve this class to auto-register to the close event and remove the stored object automatically).
public class FormsSession<T> where T : class, new()
{
private static Dictionary<string, T> _container = new Dictionary<string, T>();

public void Add(string FormUID, T FormObject)
{
if (_container.ContainsKey(FormUID))
_container[FormUID] = FormObject;
else
_container.Add(FormUID, FormObject);
}

public T Get(string FormUID)
{
if (!_container.ContainsKey(FormUID))
_container.Add(FormUID, new T());
return _container[FormUID];
}

public void Remove(string FormUID)
{
if (_container.ContainsKey(FormUID))
_container.Remove(FormUID);
}
}

Multiple specific selection in SAP B1 ChooseFromList

I recently had the need to create a selection type that would allow selection of specific values (multiple ones) from a ChooseFromList. The basic case of single selection and range selection also had to be supported.
As an example, consider the ChooseFromList from Items. I needed to be able to select A1, A3, A5 – A10.
The selection would look like A1, A3, A5, A6, A7, A8, A9, A10. In other words A2, A4 would be ignored as they were not on the selection.
To achieve this, the multiple selection on the ChooseFromList object was activated to be able to select multiple values. But, the EditText which “contains” the ChooseFromList can only contain one object and it was linked with a UserDataSource (which allows only one value to be saved). In order to save the multiple selected values a DataTable was added to the form’s data source collection.
<DataTables>
<DataTable Uid="DT_0">
<Columns/>
</DataTable>
</DataTables>


Once the DataTable was added the ChooseFromList event could now be used to save the selected values into this DataTable.
if (cflEvent.SelectedObjects != null && cflEvent.SelectedObjects.Rows.Count == 1)
{
oItem.TextStyle = (int)BoTextStyle.ts_PLAIN_FONT;
string SelectedValue = cflEvent.SelectedObjects.GetValue(e.ChooseFromListAlias, 0) as string;
d.ValueEx = SelectedValue;
}
else if(cflEvent.SelectedObjects != null && cflEvent.SelectedObjects.Rows.Count > 1)
{
oItem.TextStyle = (int)BoTextStyle.ts_BOLD;
string SelectedValue = cflEvent.SelectedObjects.GetValue(e.ChooseFromListAlias, 0) as string;
d.ValueEx = SelectedValue;

selection.CopyFrom(cflEvent.SelectedObjects);
}


Some care had to be taken in order to clear the DataTable every time the selection on this particular ChooseFromList was changed.
For this, a simple XPath expression is evaluated to check the current ChooseFromList allows for multiple selections. If so, then it clears the DataTable before saving the new selection.
DataTable selection = oForm.DataSources.DataTables.Item("DT_0");
XPathDocument doc = new XPathDocument(new StringReader(oForm.GetAsXML()));
XPathNavigator nav = doc.CreateNavigator();
string xpath = string.Format("//ChooseFromList[@UniqueID='{0}']/@MultiSelection", e.ChooseFromListUID);
XPathNodeIterator iter = nav.Select(xpath);

iter.MoveNext();
if (iter.Current.Value == "1")
selection.Clear();

After adding the values to the DataTable the user is given a hint that multiple values are selected by making the text on the EditText bold. Also if a user double clicks the text on the EditText a small Grid displays the selected objects.