Showing posts with label Word. Show all posts
Showing posts with label Word. Show all posts

Thursday, May 19, 2016

Deploy Word document generation template to new Dynamics 2016 environment.

Microsoft are treating the new document generation facility in Dynamics 2016 as the best thing since sliced bread. There are some genuine limitations with it at the moment which I've seen posted elsewhere.
But I've come across an issue with custom entities. I managed to create a template alright but then tried to install it in another environment. I wasn't trying anything fancy, just a manual install of the template. But when I added the template it bound it to the wrong entity. Now it doesn't offer me the option to choose the entity, just goes ahead and binds it to the wrong thing.
It is to due to the object type code or sometimes called the entity type code. That code is fixed for all the standard entities, but custom entities are given a number in the 10,000 range. What I've discovered is this number is not the same across environments. It's not that I've been stupid and manually created the entity, I deployed a solution as you normally do, but it issues a different type code in the new environment. You can see it easily if you can access the full url for an entity record, you know when you want to find the unique guid. The etc parameter is the entity type code.
Now this wouldn't be a problem except when you create a Word document template it adds the entity type code to the end of the namespace. you can see it when in Word and you are in the Xml mapping pane. It's there at the end of the urn. That then causes it to bind to the wrong entity when deployed into the new environment.
If you spent hours creating the template you don't want to have to repeat this in each environment.
To solve it, take the template and rename the extension from docx to zip. You will see in the extract a customxml folder and in there a file that contains the urn of the source data. You have to change the etc at the end to match the correct one in the target environment. Change any other related custom entities. Zip it back up and rename it back to docx. Load it back into the target environment and it should work. Maybe I'll find the time to write an app to do this for me because I don't see Microsoft fixing this anytime soon. Damn, sometimes Microsoft just don't think things through.

Sunday, January 26, 2014

Load a Word document into Internet Explorer and set Response.ContentType

About two years ago I wrote some code to construct a PDF document on the fly and display it in the browser. Yesterday I was trying to do the same thing for a Word document. If you've seen earlier posts this month you will see I am using Aspose Words for .Net to perform a mail merge.  I wanted the ability to preview the result before saving the output. 
 
Now when I did this previously for a PDF file I had a web service that returned a memory stream and I was able to load that into the Response.OutputStream object without difficulty. But I immediately ran into a problem with my web service which is built with .Net FW 4.0.  The memory stream I returned became a marshalled object which does not have the same properties for WriteTo() or ToArray() which meant I could not easily load it into Response.OutputStream.
 
I suppose I could have found a solution but instead I thought I would return it as a string instead. Alas that gave a new set of problems which I suspect was down to encoding when converting between the stream and the string.  This morning I tried converting to a base base64 string and that did the trick. 
 
So firstly here is the code in the web service that converts the memory stream to base64.


public string PreviewMerge(DataMergeRequest req)
{   // code to do mailmerge goes here
   MemoryStream msRawData = merge.MergeDataSet(ds, templatelocation);

   string base64;
   // ENCODE TO BASE 64
   base64 = Convert.ToBase64String(msRawData.GetBuffer(), 0, (int)msRawData.Length);

    return base64;
}

On the ASPX page you need to remove everything below the Page directive.
In the Page_Load event you need this code. Note you should NOT use Response.End - there is a known issue with it creating a Threading exception.  Use HttpContext.Current.ApplicationInstance.CompleteRequest() instead. 


Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.Charset = "";

// get results of merge as base 64 encoded stream
string strBase64 = ds.PreviewMerge(req);

// DECODE into memory stream
byte[] raw = Convert.FromBase64String(strBase64);

using (MemoryStream decoded = new MemoryStream(raw))
{
   // load the stream into the Response Output stream
   decoded.WriteTo(Response.OutputStream);
}
// set the content type for docx
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();





 

Tuesday, May 11, 2010

Converting XML to PDF

I was looking for an application that would convert an XML file into a PDF document. In fact I was trying to create an invoice as a PDF file were the invoice had originated from Great Plains (an application from the Microsoft Dynamics suite that claims to be an accounts package).

A colleague recommended Aspose.Words for .NET to me as he was using it for another project. It uses a Word document as a template and sets it up as a mailmerge. Now this made me a bit wary. I've had first hand expereince of programming a Word mailmerge and it wasn't a pleasant outcome. So the thought of doing a mailmerge in Word wasn't appealing. But it turns out that Aspose.Words for .Net has a single line API call that does the mailmerge for you. Brilliant!

doc.MailMerge.ExecuteWithRegions(GetTestOrderTotals());

But the second problem is a failing in Word 2007 - it doesn't support mailmerge with XML documents as a datasource. So I started looking at the examples that ship with Aspose.Words and stumbled on a Sales Invoice demo. In this case the Word document was using the NorthWinds Access database as the mailmerge source. In fact it was using 3 different views for the order header, the order line items and the order totals.

So how was I going to make that work with my XML source? A few minutes Googling gave me the answer. Word is simply using DataTables as the datasource so all I had to do was create a new DataTable with the same name as the one the Word document expected and create the appropriate columns with the correct data types. Then create a new row in the datatable and pass in the values extracted from the XML document. Bingo.

string Total = GetFieldFromXml(xmldoc, "eConnect/SOPTransactionType/taSopHdrIvcInsert/DOCAMNT");
// other strings declared and assigned here
ds.Tables.Add("OrderTotals");
ds.Tables[0].Columns.Add("OrderID");
ds.Tables[0].Columns.Add("Subtotal");
ds.Tables[0].Columns.Add("Freight");
ds.Tables[0].Columns.Add("Total");
ds.Tables[0].Rows.Add(OrderID, Subtotal, Freight, Total);

The order line items was easy to solve - I just created a DataRow for each node within my list of line items in the XML message.

What about creating the PDF document? Easy, just one line of code in Aspose.Words to specify that a PDF version is required and to open in the Web browser. I just did the usual trick of specifying the Response.ContentHeader to be application/pdf.

HttpResponse resp = Context.Response;
System.Text.Encoding enc = System.Text.Encoding.GetEncoding("ISO-8859-1");
resp.ContentEncoding = enc;
resp.ContentType = "application/pdf";
// now display PDF in browser
doc.Save("Aspose.Words.Demos.pdf", SaveFormat.Pdf, SaveType.OpenInBrowser, resp);

I was amazed how simple it was to create a PDF invoice. The great benefit of Aspose.Words is the use of the Word document as a template. It's a snip to modify the layout to get what you want. If you want to add extra fields you'll need to modify the views in Access to produce extra columns. No problem for me because I'm an old hand at Access but if you are unfamiliar with it then you might want to reconsider this option. But this is a great solution for my needs. Watch out though there is license cost to pay!