A few weeks ago, I described a solution for importing an Excel file to the Business Central using Excel Buffer in AL Language (see here). In today’s article, we will look at how to build a more complex solution that can be used for file imports without the needs to identify the imported file type.
The example is available on my GitHub (here). In the next parts, we will look in details at some of the parts from this repository.
Enum & Interface
First of all, we need to create an interface that describes which procedures must be implemented by each file format implementation (if you want to know more about AL Language enums & interfaces, check my previous articles about Enum Data Type or the series of Interfaces in AL (part 1), (part 2) and (part 3)).
For our example, we need only two procedures. You will probably need some more procedures to make a (super) universal solution in real extension, but two methods are enough for our example.
So every implementation that uses this interface has to implement ImportFile procedure (that accept Code[20] and returns temporary CSV Buffer) and FieldSeparatorSupported procedure (returns boolean).
/// <summary>
/// Interface TKA IImport File Format.
/// </summary>
interface "TKA IImport File Format"
{
/// <summary>
/// Import lines from the select file. The file is selected by user. Based on implementation, some other selections may be neccessary (specific sheet/list/range, ...).
/// </summary>
/// <param name="ImportConfigurationCode">Code[20], definition of used import configuration</param>
/// <returns>Return value of type Record "CSV Buffer" temporary, contains all lines from imported file.</returns>
procedure ImportFile(ImportConfigurationCode: Code[20]) TempCSVBuffer: Record "CSV Buffer" temporary;
/// <summary>
/// Specifies whether field separator is supported for the format
/// </summary>
/// <returns>Return value of type Boolean.</returns>
procedure FieldSeparatorSupported(): Boolean;
}
We will also create an enum that defines our supported file types. For now, we will add support for Excel (xlsx) and CSV files. The important thing is to define enum using the keyword “implements” followed by the name of our interface. This construction specifies that all enum values have to implement this interface (it does not matter whether the value is included in the Enum definition or is added through EnumExtension).
/// <summary>
/// Enum TKA Import File Format (ID 50000) implements Interface TKA IImport File Format.
/// </summary>
enum 50000 "TKA Import File Format" implements "TKA IImport File Format"
{
Extensible = true;
value(5; "Excel file (.xlsx)")
{
Caption = 'Excel file (.xlsx)';
Implementation = "TKA IImport File Format" = "TKA Import File Format XLSX";
}
value(10; "CSV file (.csv)")
{
Caption = 'CSV file (.csv)';
Implementation = "TKA IImport File Format" = "TKA Import File Format CSV";
}
}
Interface Implementations
Interfaces can be implemented using Codeunits with the “implements” keyword (similarly to enums).
CSV Files (Code on GitHub)
There is nothing special about the implementation of CSV file imports. The import is done using “CSV Buffer” functionality (that manage everything internally) and, as the ImportFile procedure must return CSV Buffer, we just can return the loaded records.
The procedure FieldSeparatorSupported returns true as CSV files needs separator character to split the values.
Excel Files (Code on GitHub)
The implementation of Excel files is very similar. It is done using Excel buffer (as was discussed in this article). The only difference from CSV file implementation is that we need to change the structure of loaded values from “Excel Buffer” to “CSV Buffer”.
FieldSeparatorSupported returns false as Excel does not use separator characters.
Finally, let’s import file
Once we have defined and implemented all supported file types, we can use our functionality just by calling the ImportFile procedure through an interface defined with the enum value.
For our example, I created a table that defined file configuration (file type and field separator, if necessary), available on GitHub. Using values in this table, we know which file type we are importing and which field separator we are using.
/// <summary>
/// Import lines using import configuration form current Record (Rec)
/// </summary>
/// <returns>Return value of type Record "CSV Buffer" temporary, contains all lines from imported file.</returns>
procedure ImportFile(): Record "CSV Buffer" temporary
var
IImportFileFormat: Interface "TKA IImport File Format";
begin
IImportFileFormat := "TKA Import File Format";
exit(IImportFileFormat.ImportFile(Rec."TKA Code"));
end;