Its Been a while

Wow, long time since my last post. I guess its a mix of being busy on projects and being completely uninspired when it comes to writing any thing up here. This occurs mostly in the winter when I feel drained, and this winter has been pretty bad on me when it comes to my mental health. Thankfully we press through and just get stuff done.

Here at BITtelligent we focus on so many different things, and at times its hard to determine where the next focus point needs to be. Being a jack of all, with some razor focus on the SalesLogix product line I sometimes get lost without regards to the bigger picture. BITtelligent has become quite successful of late of SalesLogix web upgrades and almost have it down to a Science.

Over the next few months I hope to provide more details on the BITtelligent Integration Toolkit. A platform and set of services for creating custom/standalone integrations to various systems. Shortly we will be delivering our first mini-integration providing Microsoft Exchange to SalesLogix Ticket and History. We are very excited by these mini platform releases and hope to have a series of releases each year.

I will also be delving into SalesLogix 8. I have been fortunate enough to work with the team in Scottsdale on the product and provide hopefully great insight on how to get the most out of the platform.

Finally we at BITtelliigent will be adding some external competencies around ERP synchronizations specifically MAS90/200 and MAS500 through Dynalink and ERPLink product lines. Focusing on aiding support to the business partner and end customer community to get running and maintaining a working synchronization process.

There’s a Service for That

A question came up yesterday with regards to how to create SalesLogix ids within the Web Framework. In the legacy client and our external development we would generally call a provider Stored procedure that would give us the Id. The code might look as the following

List<string> list = new List<string>();

using (
       var cmd = new OleDbCommand(string.Format("slx_dbids('{0}', {1})", table, count), 
       connection)
)
{

   var r = cmd.ExecuteReader();

   while (r.Read())
   {
      list.Add(r.GetString(0));
   }

    r.Close();
 }

return list;

Now within the SalesLogix web client infrastructure it is a actually easier if you know where to look.

The framework exposes a service – SalesLogixEntityKeyGenerator. You can get access to this service using the following code;

SalesLogixEntityKeyGenerator generator = 
ApplicationContext.Current.Services.Get<SalesLogixEntityKeyGenerator>(false);

 

Once you have access to the generator it its very easy to get a set of keys;

List<string> keys = new List<string>(generator.GenerateIds(typeof(IHistory), 1));

 

There you have it.

Sage SalesLogix Mobile Pick Lists (BlackBerry)

When working with Sage mobile from time to time you will find that there is missing functionality that you expected to be there but is not. One such piece of functionality involve pick lists and recently worked on a project that required ‘must exist in list’ functionality. By the way did I say how much I like using Eclipse when I have to do java development. Well I do.

Must Exist In List

MustExistInList is a property of Sage Saleslogix web and Lan client picklist. This ensures that the end user cannot enter any free form text into the pick list edit area. This property however has yet to make it up to the mobile implementation. Since the property does not exist and it is not possible to introduce new controls into the form building process I had to take a more intermediate step. The first thing to remember is that most of the controls that exist on the BlackBerry device start as a HorizontalFieldManager.

Generally the following layout for each control is followed

< Label Field > + < Seperator Field > +  < Edit Field >

however with the lookups and picklists the following layout is followed

<Label Field> + < CMPImage > + < Seperator > + < Edit Field >

Now I was not completely sure on the layout of the control, so to help me on a way I wrote a simple helper method that did nothing more then to iterate through the embedded fields on the HorizontalLayoutManager and inform me of the embedded class names.

Once I had this information in hand I built out a pick list adapter method. I should have created a full adapter to allow for future expansion however sometimes KISS should be honored especially if under the gun to deliver in short order.

Its a simple method but effective in providing the functionality.

public static void setPicklistEditable(CMPPickList field, boolean editable ) {
    HorizontalFieldManager manager =  (HorizontalFieldManager) field;
    EditField edit = (EditField)manager.getField(3);
    if (edit != null) {
        edit.setEditable(editable);
    }
}


Note that the index for the field is 3 even though there is 4 embedded controls as arrays in java are 0 based .

Customer Portal

I am Working on a Customer Portal project and I was seeing some strange behavior. When ever I opened up a Lookup control a JavaScript  error was thrown and the resulting data was not being displayed. However after CTRL+F5 on the page the values would then show up. After discussing the problem and looking at the issues inside of Firebug to see that there was no named query available for the results to be generated. I was then pointed to the right place where the customer portal service list did not contain 2 very important entries.

So in a nutshell, ensure that in the customer portal the following 2 services are registered:

#1

Service: Sage.Platform.NamedQueries.DictionaryBasedNamedQueryCacheService, Sage.Platform

Registered As: Sage.Platform.NamedQueries.INamedQueryCacheService, Sage.Platform

#2

Service: Sage.Platform.NamedQueries.DictionaryBasedNamedQueryCacheService, Sage.Platform

Registered As: Sage.Platform.NamedQueries.INamedQueryLookupService, Sage.Platform

 

Once the entries were added all of the lookups in customer portal worked as expected.

Mark

IDataService and Utility Methods

This morning I got into a IM discussion with Alexander Pfingstl. He works for for a BP in Germany. We were discussing the ability to handle custom address entry from the Add Contact Account screen in SalesLogix web. Since the format of German address layout is different then that of North America custom work needed to be done. Given that the current incarnation of the address control does not allow for customization it was not possible to make the changes there. Also using the Add/Edit address dialog was not possible because it works off an existing entity (account/contact) and not one that has yet to be created.

I had suggested that he just place the address details directly on the Add Contact screen and bind it directly. I have done this before and it works like a charm.

The next question that came up was how to call a business rule with out an entity. It seems that he has code that does a City lookup based on ZIP/Postal information. I am sure that we all have some form of this code around. What struck with me is that this code is not specifically entity bound and is more a utility method then a business rule. Really when you look at it from a consultant role this code should be as generalist as possible for maximum reuse.

As with most things I do, I recommended to create an external library in Visual Studio. I know, outside if AA where you may be saying that we should try to keep inside of the SalesLogix dev environment. This is were I would disagree. The goal is to create value for both the current customer, and others in the future.

In our discussions we talked about the DataService. With this service it is possible to get the underlying connection string to the SalesLogix database. You can also get a connection but I shy away from that as I like to know when it is created and destroyed so using the connection string gives me this flexibility.

So finally I opened up VS and a web portal and started to chunk out a code sample for Alexander and provided this code:

   1: public string GetCityFromZip(string zip)

   2: {

   3:     string result = string.Empty;

   4:     IDataService service = ApplicationContext.Current.Services.Get<IDataService>();

   5:     using (var connection = new OleDbConnection   (service.GetConnectionString()))

   6:     {   

   7:         connection.Open();   

   8:         using (var command = connection.CreateCommand())

   9:         {         

  10:             command.CommandText = "Select City from CityZipTable where Zip = ? "; 

  11:             command.Parameters.Add(new OleDbParameter("@Zip", zip));           

  12:             result = (string)command.ExecuteScalar();      

  13:         }           

  14:     }    

  15:     return result; 

  16: }

Now this code is specific to SalesLogix web as it uses the data service. To truly make it universal what should be done is a simple refactor to pass in the connection string instead of deriving it from the Data Service.

   1: public string GetCityFromZip(string connectionString, string zip)

   2: {   

   3:     string result = string.Empty;    

   4:     using (var connection = new OleDbConnection(connectionString))   

   5:     {      

   6:         connection.Open();       

   7:         using (var command = connection.CreateCommand())      

   8:         {         

   9:             command.CommandText = "Select City from CityZipTable where Zip = ? ";

  10:             command.Parameters.Add(new OleDbParameter("@Zip", zip));

  11:             result = (string)command.ExecuteScalar();      

  12:         }   

  13:     }   

  14:  

  15:     return result;

  16: }

 

So now this method can be use from SalesLogix or an external application. Note the use of parameterized query to ensure that we do not get a SQL injection issue. So to call it from a SalesLogix web you can just create the following code:

   1: public void OnZipChanged(object sender, EventArgs args)

   2: {   

   3:     AddressUtilities utilities = new AddressUtilities(); 

   4:     string connectionString = ((IDataService)ApplicationContext.Current.Services.Get<IDataService>).GetConnectionString();    

   5:     txtCity.Text = utilities.GetCityFromZip(connectionString, txtZipPostal.Text);

   6: }

And using it from an external application its as simple as:

   1: public void UpdateCityBasedOnZip(string zip)

   2: {   

   3:     string connectionString = "<connection string here>";   

   4:     AddressUtilities utilities = new AddressUtilities();   

   5:     txtCity.Text = utilities.GetCityFromZip(connectionString, zip);

   6: }

So there you go, a library approach.

Hope this helps.

My Kung Fu is Strong

Ya, a goofy title, I agree.

This post is somewhat a rant and I hope that it is received well. I generally get many, many requests on how to do something on SalesLogix web platform, or coding in general. I have not really a problem with the questions that are directed as could you point me in the right direction, or what are your thoughts. I do however have a problem with the requests that come in for full working examples of functionality. The reason I have problems is that for the most part its pushing the development and and training effort on me. The fact that I may have the ability to create the solution quicker does not negate that there are real hard costs to making the solution work, generally of which the requester is working on a billing project. Creating this examples can take anywhere from an hour to many hours, I have even had request for work that would take days to provide a solution. For the most part those who also request are not supporters of the community and are only looking for a quick solution to something that is racking their brain.

This is where I remind those who request these working samples that software development is what I do. These things you request are things that I have personally had to learn and invest my time into. I am in no way compensated by Sage to support the community, there is no MVP program, or recognition system. I have to pay to be a partner, for my development software and all of the books that I read to ensure that I keep ahead of the curve. The other thing to keep in mind that I have work commitments like the rest of you to deliver code as expediently as possible and creating these samples pushes billable hours, and can encroach on the little family time left.

I have been toying with a couple of ideas, and would appreciate some feedback to what you dear reader think of the feasibility.

1. Create and distribute a monthly SalesLogix web Journal for a sum of $ per year (Good Idea, what would you be willing to pay)

2. Create a training video subscription that includes web, mobile, C#, debugging topics (Good Idea, what would you be willing to pay)

3. Developer on hand program. For those that only need a few hours per month a way to have direct access and get the coding samples you need but would pay for them

4. Fly on the Wall training. When doing an engagement having web ex meetings on specific items to have a developer watch and mentor around the most difficult parts to be delivered.

5. Architectural review and pre-engagement consulting

I really think that all 5 are advantages for any BP looking to be able to do more web business even if they do not have the core developer completely up to speed on the web development. It also allows you to tie the costs to a development and not have more bench time to finance.

Some of the other things I am trying to gauge and hopefully you can provide some feed back is around specific community needs.

1. Do you see a need for Mobile Development Book if so would you buy one and what would your expectation of price.

2. Do you see a need for more mobile training outside of what Sage provides.

3. Do you see a need for virtual events where several industry notables (Ryan Farley, Stephen Redmond, and Me ) provide special event training. Is this something you would attend?

4. What gaps do you see in tooling. documentation, training, support, and development do you see that would make your life much easier to deliver compelling solutions.

If something I provide helps you please also do send me a small email and let me know. Its always good to hear feedback.

Thanks for reading, If you have any thoughts please email me at mark.dykun@BITtelligentdev.com or give me a call at 519.260.0999. 

SalesLogix 7.5 RC2 Is Released

Yesterday night RC2 for SalesLogix was released out to the Business Partner community. The 7.5 release in my opinion represents the best release for the Web Client yet. I have to say I am getting excited that the product is getting ready for final release and SalesLogix customers will be able to get their hands on the product. It looks so much better then previous versions. The development team has been working hard and this is a great step forward.

SalesLogix Web Client Working on Vista 64

Earlier I posted on the fact that getting the SalesLogix web client working on vista not quite possible at this time. I am really happy to say that after some poking, a little log browsing an a google search I now have it working correctly.

To figure out what was going wrong for me I turned on assembly binding logs (fuslogvw.exe) , choosing to only log failures. I set the log path to a simple c:\log directory. Since I really only cared about the failure at this time I turned on ‘Only log bind failures to disk’.

Running this utility will add some settings into the registry under HKLM\Software\Microsoft\Fusion

DWORD LogFailures  – will store a value of 1 meaning to log bindings (you can change this to 0 to disable logging)

SZ LogPath – Location to log the binding failures

I then tried to open the web site where an error was reported. I went into the log folder that I designated  and noticed a file for dtSearchNetApi2 and opened it discovering that the assembly was found but a HRESULT was being returned with the value of 0x800700b. This is where google came in and I discovered that the issue I was having was with regards to 64 bit app pool and 32 bit interop.

More so I was able to easily find an answer to my problem and got it working.

So, to get it working on a 64 bit machine there are 2 things you have to do;

1. Ensure that the site AppPool is setup with Classic Asp.net

2. On 64 bit machines since there is some COM Interop interacting with 32 bit dll’s you will need to enable 32 bit application pools.

Type the following at a command line prompt

cscript %SystemDrive%\inetpub\AdminScripts\adsutil.vbs set w3svc/AppPools/Enable32bitAppOnWin64 1

To turn off the 32 bit application pool change the last value to a 0

cscript %SystemDrive%\inetpub\AdminScripts\adsutil.vbs set w3svc/AppPools/Enable32bitAppOnWin64 0

Thanks to Ryan Hoffman for information about this setting.

– Mark