TechEd 2007 InfoPath demo bits.

At TechEd in Orlando a few weeks ago, I did a demostration of how InfoPath can be used as an easy client for submitting XML documents to Dynamics AX. The only glue you need to provide is a simple web service that pushes the XML document into the business connector, as shown below:

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using Microsoft.Dynamics.BusinessConnectorNet;

[WebService(Namespace = https://www.villadsen.dk/ConsumesInfopath)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class InfoPathConsumer : System.Web.Services.WebService
{

public InfoPathConsumer()
{

}
[WebMethod]
public string ConsumeDocument(string s)
{

// Put the XML into the application through the business connector.
Axapta ax = new Axapta();
try
{

ax.Logon("", "", "", "");
string retval = (string)ax.CallStaticClassMethod("InfoPathPortal", "TravelExpense", s);

}
finally
{

ax.Logoff();

}
return s;

}

}

You install this as a web service, and then simply tell Infopath to submit its captured data to it. I think you'll agree that there's nothing really revolutionary about that code. It's pretty standard in the way it uses the business connector to gain access to a class, posting the incoming document into Dynamics AX (through the TravelExpense static method). For your reference, I'm listing the content of that method below for your reading pleasure(?) Please bear in mind that this is definitely NOT poduction code (it was written in the conference dinner hall before the session in which it was used), but it does show some interesting techniques in dealing with XML documents.

public static str TravelExpense(xml documentContent)
{

TravelExpenses expenses;
TravelerPreference preferences;

TextBuffer tb = new TextBuffer();
XmlDocument doc = new XmlDocument();
XmlNodeList trips;
XmlNode trip;
XmlParseError parseerror;
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable());
int tripno;
str purpose, name, email;
str departureLocation, destination;
Date requestDate, departureDate, returnDate;
TimeBracket departureTime, returnTime ;
boolean includeHotel, includeCar, isRoundTrip;
TripClass tripClass;        // "Economy / Coach", Business, First
SeatLocation seatLocation;  // Aisle, Window, Middle
CarClass carClass;          // Compact, "Full Size" and Luxury
boolean nonSmokingRoom;
str notes;
str errorMessage;

Date GetDateFromString(str s)
{

str y = substr(s,1,4);
str m = substr(s, 6, 2);
str d = substr(s, 9, 2);

return mkDate(str2int(y), str2int(m), str2int(y));

}

boolean GetBoolFromString(str s)
{

return s == "true";

}

TripClass GetTripClassFromString(str s)
{

if (s == "Economy / Coach") return TripClass::Economy;
else if (s == "Business") return TripClass::Business;
else return TripClass::First;

}

SeatLocation GetSeatLocationFromString(str s)
{

if (s == "Aisle") return SeatLocation::Aisle;
else if (s == "Window") return SeatLocation::Window;
else return SeatLocation::Middle;

}

CarClass GetCarClassFromString(str s)
{

if (s == "compact") return CarClass::Compact;
else if (s == "Luxury") return CarClass::Luxury;
else return CarClass::FullSize;

}

TimeBracket GetTimeBracketFromString(str s)
{

if (s == "Morning") return TimeBracket::Morning;
else if (s == "Afternoon") return TimeBracket::Afternoon;
else if (s == "Evening") return TimeBracket::Evening;
else return TimeBracket::AnyTime;

}
;

if (!doc.LoadXml(documentContent))
{

parseerror = doc.parseError();
errorMessage = strfmt("Something's wrong: %1", parseerror.reason());
info(errorMessage);
info(documentContent);
return errorMessage;

}

// Create a namespace manager to maintain the mapping from prefixes to URIs
namespaceManager.AddNamespace("my", "https://schemas.microsoft.com/office/infopath/2003/myXSD/2005-10-21T21:12:27");

purpose = doc.DocumentElement().SelectSingleNode("//my:purpose", namespaceManager).innerText();
requestDate = GetDateFromString(doc.DocumentElement().SelectSingleNode("//my:requestDate", namespaceManager).InnerText());
name = doc.DocumentElement().SelectSingleNode("//my:name", namespaceManager).InnerText();
email = doc.DocumentElement().SelectSingleNode("//my:email", namespaceManager).InnerText();

tripClass = GetTripClassFromString(doc.DocumentElement().SelectSingleNode("//my:tripClass", namespaceManager).InnerText());
seatLocation = GetSeatLocationFromString(doc.DocumentElement().SelectSingleNode("//my:seatLocation", namespaceManager).InnerText());
carClass = GetCarClassFromString(doc.DocumentElement().SelectSingleNode("//my:carClass", namespaceManager).InnerText());

nonSmokingRoom = GetBoolFromString(doc.DocumentElement().SelectSingleNode("//my:nonSmokingRoom", namespaceManager).InnerText());
notes = doc.DocumentElement().SelectSingleNode("//my:notes", namespaceManager).InnerText();

// For all the trips: trips = doc.DocumentElement().SelectNodes("//my:trip", namespaceManager);
for (tripno = 0; tripno < trips.length(); tripno++)
{

trip = trips.item(tripno);
departureLocation = trip.SelectSingleNode("./my:departureLocation", namespaceManager).InnerText();
destination = trip.SelectSingleNode("./my:destination", namespaceManager).InnerText();

departureDate = GetDateFromString(trip.SelectSingleNode("./my:departureDate", namespaceManager).InnerText());
// This can be Anytime, Morning, Afternoon, Evening
departureTime = GetTimeBracketFromString(trip.SelectSingleNode("./my:departureTime", namespaceManager).InnerText());

isRoundTrip = GetBoolFromString(trip.SelectSingleNode("./my:isRoundTrip", namespaceManager).InnerText());
if (isRoundTrip)
{

returnDate = GetDateFromString(trip.SelectSingleNode("./my:returnDate", namespaceManager).InnerText());
returnTime = GetTimeBracketFromString(trip.SelectSingleNode("./my:returnTime", namespaceManager).InnerText());

}
includeHotel = GetBoolFromString(trip.SelectSingleNode("./my:includeHotel", namespaceManager).InnerText());
includeCar = GetBoolFromString(trip.SelectSingleNode("./my:includeCar", namespaceManager).InnerText());

}

// Get the preferences. Update the user's preferences if they're new.
ttsBegin;
expenses.clear();
expenses.DepartureDate = departureDate;
expenses.DepartureTime = departureTime;
expenses.DepartureLocation = departureLocation;
expenses.Destination = destination;
expenses.Name = name;
expenses.Notes = notes;
expenses.Purpose = purpose;
expenses.ReturnDate = returnDate;
expenses.ReturnTime = returnTime;
expenses.write();
ttsCommit;

return "Hello from Ax";

}

For the sake of completeness, I'm including the XML file that InfoPath generates:

<?xml version="1.0" encoding="UTF-8"?><?mso-infoPathSolution solutionVersion="1.0.0.4" productVersion="12.0.0" PIVersion="1.0.0.0" href="file:///C:\Users\pvillads\Desktop\TravelExpenseTemplate.xsn" name="urn:schemas-microsoft-com:office:infopath:TravelExpenseTemplate:-myXSD-2005-10-21T21-12-27" ?><?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?><my:travelRequest xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="https://www.w3.org/1999/xhtml" xmlns:dfs="https://schemas.microsoft.com/office/infopath/2003/dataFormSolution" xmlns:tns="https://www.villadsen.dk/ConsumesInfopath" xmlns:my="https://schemas.microsoft.com/office/infopath/2003/myXSD/2005-10-21T21:12:27" xmlns:xd="https://schemas.microsoft.com/office/infopath/2003" xml:lang="en-us">
 <my:purpose>TechEd 2007</my:purpose>
 <my:requestDate>2007-06-28</my:requestDate>
 <my:name>Peter Villadsen</my:name>
 <my:email>pvillads@microsoft.com</my:email>
 <my:trips>
  <my:trip>
   <my:departureLocation>Seattle, SEATAC</my:departureLocation>
   <my:destination>Orlando International</my:destination>
   <my:departureDate>2007-06-28</my:departureDate>
   <my:departureTime>Anytime</my:departureTime>
   <my:isRoundTrip>false</my:isRoundTrip>
   <my:roundTrip>
    <my:returnDate xsi:nil="true"></my:returnDate>
    <my:returnTime>Anytime</my:returnTime>
   </my:roundTrip>
   <my:includeHotel>true</my:includeHotel>
   <my:includeCar>false</my:includeCar>
  </my:trip>
 </my:trips>
 <my:preferences>
  <my:tripClass>Economy / Coach</my:tripClass>
  <my:seatLocation>Aisle</my:seatLocation>
  <my:carClass></my:carClass>
  <my:nonSmokingRoom>true</my:nonSmokingRoom>
 </my:preferences>
 <my:notes>Need to book this ASAP please</my:notes>
 <my:filterDepartureLocation></my:filterDepartureLocation>
 <my:filterDestination></my:filterDestination>
 <my:filterDepartureDate></my:filterDepartureDate>
</my:travelRequest>