Month: November 2017

HuLib

HuLib is a library we have developed for our internal use in consulting applications. The purpose of HuLib was to avoid duplication in our consulting programs by abstracting it in an easy to use library.

The majority of our applications are written in C# and deal with AccPac, file reading/writing and SQL interaction (both with csqry and otherwise) so this is where the main focus of the library’s features lie.

Sage (AccPac) features

A complication with our writing of Sage consulting applications has been AccPac’s API. While very versatile, the API has an awkward syntax which requires a non-standard form of error detection and in the case of CSQRY (which is used to send sql queries through the Sage API) requires setting properties without explanation for it to work.

To deal with this we have abstracted away the Sage connection (you can now create a new connection with

 Connections

HuAPConnection connection = new HuAPConnection(); // OR
HuAPConnection connection = new HuAPConnection("ADMIN", "ADMIN", "SAML64");

The first line above creates a new connection via the sign on manager, the second one lets you set the user / password / company manually.

HuAPConnection is Disposable (you can have it in a using statement and it will dispose of it once it is out of scope) and is the focal point for sage interactions from this point on.

Views

using (HuView arcus = connection.OpenView("AR0024"))
{
    arcus["IDCUST"] = "1200";
    if (arcus.Read())
    {
        arcus["TEXTSNAM"] = arcus["TEXTSTRE1"] // set name to address1;
        view.Update();
    }
}

View interactions are similar to using the accpac view objects directly but it is wrapped by an HuView and is retrieved from an HuAPConnection. Like connections, HuViews are disposable, however the connection also keeps track of all opened views so if you had not disposed of the view, when the connection closes it will close it for you!

In addition to OpenView, there is another similar function that returns a view called GetView, this allows you to repeatedly use the same view (if there was one that has been disposed it will clear that view and return it to you. This allows you to have a GetView function within a for loop and you do not have to worry about the overhead on creating new views, the connection will notice there is now an available view of the type you requested, clear it and return it to you.

While the raw Sage view is still accessible from the HuView, most interactions will use HuView directly. As seen above, fields are assigned and retrieved via an indexor rather than the traditional way with the Sage api: <view>.Fields.FieldByID[<id>].set_Value(<value>) which can be a bit overkill and makes code less readable when you have many of these operations.

Additionally, when performing operations with the HuView, any exceptions will be raised as an AccPacException. This way you do not need to deal with catching COMExceptions and reading from the Sage api directly the error / warning feeds which was what we had to do previously and adds much more boilerplate code than we would want.

UI

We use mainly WPF for our desktop applications now. Using the accpac finder has presented us with many issues and as such we have developed our own version.

<hulib:HuWFEC Connection="{Binding Connection}" ViewID="BK0001" Value="{Binding Bank}"/>

The above is a line taken from one of our applications using it. You can bind to connection, value, filter. You can also set field ids that are displayed and many other settings you would expect to be present for an AccPac finder.

huwfec

It looks very similar to the Sage finder (the top of the image shows the textbox and button to open the finder, the pop up below is what is shown when the button is clicked.

Other Features

There are many other quality of life features (eg a function that creates a dictionary based on optional field key-value pairs). In addition in the sections below there are features that help integrate back into AccPac (eg Mapping)

File IO

Often our customizations revolve around exporting or importing data to/from files. To assist in our development we have abstracted some of this functionality.

Mapping

One of the commonalities between the different forms of files and even SQL and Sage views is that there is a feature of HuLib we called simply Mapping that will work with them. Mapping is a way to use annotations on your code to very easily extract data from different sources. For example, see: Reading CSV files

This way changes in the data source can be dealt with in the model that will store the data directly instead of having to both change the model and the code that would normally be importing it.

CSV Files

See: Reading CSV files

Using the CSVFile class you can easily load a file with just the file name then you have it all split for you (includes custom delimiters, escape character parsing). On top of that there is a mappable implementation meaning you can use mapping to just label a model class you want the data to go into and have it populated in just a couple lines.

Excel Files

See: Reading Excel Files

We have a number of different excel utilities. The latest one we have been using is the BufferedExcelReader (also, writer). Using this you do not have to manually interact with the excel interop API and can instead just use ReadString, ReadLine as if it was a standard file. If you want to access specific parts of the sheet, you can do it based on column/row with the column being in standard excel letter form or in numerical form. Or you can use the mapping implementation to immediately load data from excel into a collection of your model.

One of the main advantages of the BufferedExcelReader over our previous utilities is the buffering portion. Interop actions between excel are expensive, so the BufferedExcelReader deals with this by preloading the whole file in bulk and allowing you to interact with it in the way you want.

Tools

We have developed a number of tools to help generate code that works with HuLib immediately. These include a website whereby uploading a CSV file it provides you with a model class for that file complete with the mapping annotations. A web based view info for Sage information that allows searching. A web based application that can convert the generated macro code from AccPac into C# and then into HuLib code (Most sage code in the various blog posts here are a result of running macro code through this converter and pulling out the needed parts).

And More!

This is just a summary of some of the main features of HuLib, since we are using this as a general library for our commoon requirements from day to day we are adding more to it all the time. It would be difficult to enumerate every feature present in it.

Advertisements

Common Implementations – AP Invoice Import

Snippet:

public string Import(IEnumerable<InvoiceLine> lines)
{
	// TODO: To increase efficiency, comment out any unused DB links.
	using (HuAPConnection Connection = new HuAPConnection())
	using (HuView APIBC = Connection.GetView("AP0020"))
	using (HuView APIBH = Connection.GetView("AP0021"))
	using (HuView APIBD = Connection.GetView("AP0022"))
	using (HuView APIBS = Connection.GetView("AP0023"))
	using (HuView APIBHO = Connection.GetView("AP0402"))
	using (HuView APIBDO = Connection.GetView("AP0401"))
	{
		APIBC.Compose(APIBH);
		APIBH.Compose(APIBC, APIBD, APIBS, APIBHO);
		APIBD.Compose(APIBH, APIBC, APIBDO);
		APIBS.Compose(APIBH);
		APIBHO.Compose(APIBH);
		APIBDO.Compose(APIBD);
		APIBC.Browse("((BTCHSTTS = 1) OR (BTCHSTTS = 7))");
		using (HuView APIVPT = Connection.GetView("AP0039"))
		{
			APIBC.RecordCreate(1);
			APIBC["PROCESSCMD"] = "1";  // Process Command Code
			APIBC.Process();
			APIBC.Read();
			APIBC["BTCHDESC"] = "Generated invoice batch";
			APIBC.Update();

			// Group invoices by vendor num and doc num
			foreach (IGrouping<string, InvoiceLine> invoice in lines.GroupBy(line => line.VendorNumber + " - " + line.DocuementNumber))
			{
				APIBH.RecordCreate(2);
				APIBD.Cancel();
				APIBH["IDVEND"] = invoice.First().VendorNumber;  // Vendor Number
				APIBH["PROCESSCMD"] = "7";  // Process Command Code
				APIBH.Process();
				APIBH["PROCESSCMD"] = "4";  // Process Command Code
				APIBH.Process();
				APIBH["IDINVC"] = invoice.First().DocuementNumber;  // Document Number
				APIBH["DATEINVC"] = invoice.First().DocumentDate;
				APIBH["DATEBUS"] = invoice.First().PostingDate;

				// Clear default detail lines - we only want lines based on import file
				while (APIBD.Fetch())
				{
					APIBD.Delete();
				}

				foreach (InvoiceLine invoiceLine in invoice)
				{
					APIBD.RecordClear();
					APIBD.RecordCreate(0);
					APIBD["PROCESSCMD"] = "0";  // Process Command Code
					APIBD.Process();
					APIBD["IDGLACCT"] = invoiceLine.Account;  // G/L Account
					APIBD["AMTDIST"] = invoiceLine.Amount;  // Distributed Amount
					APIBD.Insert();
				}

				APIBH["AMTGROSTOT"] = -(decimal)APIBH["AMTUNDISTR"];  // Document Total Including Tax
				APIBH.Insert();
			}
		}
		return APIBC["CNTBTCH"].ToString();
	}
}

 

Explanation:

APIBC – AP Invoice batches

APIBH – AP Invoice header

APIBD – AP Invoice detail line