Table of Contents

Using FlexCel with TMS Aurelius (Delphi)

Note

This demo is available in your FlexCel installation at <FlexCel Install Folder>\Demo\Delphi\Modules\20.Reports\A1.TMS Aurelius and also at https:​//​github.​com/​tmssoftware/​TMS-​FlexCel.​VCL-​demos/​tree/​master/​Delphi/​Modules/​20.​Reports/​A1.​TMS Aurelius

Overview

You can run a report in data from TMS Aurelius the same way you would run a report from a TList<T>.

Concepts

  • There is no need to use TAureliusDataSet. FlexCel can bind directly to the TList<T> managed by Aurelius.

  • Aurelius has 2 specific types that must be handled differently: Nullable<T> and TBlob. In the unit AureliusFlexCelSupport, we add support for those types in FlexCel. To run your own reports with Aurelius, copy the unit "AureliusFlexCelSupport.pas" in your own app, and call SetupAurelius(Report) after creating the TFlexCelReport instance.

Files

AureliusFlexCelSupport.pas

unit AureliusFlexCelSupport;
interface
{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
implementation
{$ELSE}
uses RTTI, FlexCel.Report, Aurelius.Types.Blob, Aurelius.Mapping.RttiUtils;

procedure SetupAurelius(const Report: TFlexCelReport);

implementation
uses TypInfo, StrUtils;

function ConvertAureliusTypes(const v: TFlexCelDataConversionArgs; out r: TReportValue): boolean;
var
  Blob: TBlob;
  TName: string;
  v0: TValue;
  RttiType: TRttiType;
  FieldHasValue, FieldValue: TRttiField;
  HasValue: boolean;

begin
  v0 := v.v;
  Result := false;

  if v0.IsEmpty then
  begin
    r := TReportValue.Empty;
    exit(true);
  end;

  if (v0.Kind <> tkRecord) then exit(false);

  if v0.TryAsType<TBlob>(Blob) then
  begin
    r := Blob.AsBytes;
    exit(true);
  end;


  if (v0.TypeInfo = nil) then exit(false);

  TName := GetTypeName(v0.TypeInfo);
  if StartsStr('Nullable<', TName) then
  begin
    RttiType := v.Rtti.GetType(v0.TypeInfo);
    FieldHasValue := RttiType.GetField('FHasValue');
    if FieldHasValue = nil then exit(false);
    FieldValue := RttiType.GetField('FValue');
    if FieldValue = nil then exit(false);

    HasValue := FieldHasValue.GetValue(v0.GetReferenceToRawData).AsBoolean;
    if not HasValue then
    begin
      r := TReportValue.Empty;
      exit(true);
    end;

    r := TReportValue.Create(FieldValue.GetValue(v0.GetReferenceToRawData).AsVariant);
    exit(true);

  end;
end;

procedure SetupAurelius(const Report: TFlexCelReport);
begin
  Report.DataConversionEvent :=
      function (const v: TFlexCelDataConversionArgs; out r: TReportValue): boolean
      begin
        Result := ConvertAureliusTypes(v, r);
      end;
end;

{$IFEND}
end.

DataModel.pas

unit DataModel;

interface
{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
implementation
{$ELSE}

uses
  SysUtils, Generics.Collections, Aurelius.Mapping.Attributes, Aurelius.Types.Blob, Aurelius.Types.DynamicProperties, Aurelius.Types.Nullable, Aurelius.Types.Proxy, Aurelius.Criteria.Dictionary;

type
  TEmployees = class;
  TOrders = class;
  TShippers = class;
  TEmployeesTableDictionary = class;
  TOrdersTableDictionary = class;
  TShippersTableDictionary = class;

  [Entity]
  [Table('Employees')]
  [Id('FEmployeeID', TIdGenerator.IdentityOrSequence)]
  TEmployees = class
  private
    [Column('EmployeeID', [TColumnProp.Required, TColumnProp.NoInsert, TColumnProp.NoUpdate])]
    FEmployeeID: integer;

    [Column('LastName', [TColumnProp.Required], 20)]
    FLastName: string;

    [Column('FirstName', [TColumnProp.Required], 10)]
    FFirstName: string;

    [Column('Title', [], 30)]
    FTitle: Nullable<string>;

    [Column('TitleOfCourtesy', [], 25)]
    FTitleOfCourtesy: Nullable<string>;

    [Column('BirthDate', [])]
    FBirthDate: Nullable<TDateTime>;

    [Column('HireDate', [])]
    FHireDate: Nullable<TDateTime>;

    [Column('Address', [], 60)]
    FAddress: Nullable<string>;

    [Column('City', [], 15)]
    FCity: Nullable<string>;

    [Column('Region', [], 15)]
    FRegion: Nullable<string>;

    [Column('PostalCode', [], 10)]
    FPostalCode: Nullable<string>;

    [Column('Country', [], 15)]
    FCountry: Nullable<string>;

    [Column('HomePhone', [], 24)]
    FHomePhone: Nullable<string>;

    [Column('Extension', [], 4)]
    FExtension: Nullable<string>;

    [Column('Photo', [TColumnProp.Lazy])]
    FPhoto: TBlob;

    [Column('Notes', [TColumnProp.Lazy])]
    FNotes: TBlob;

    [Column('PhotoPath', [], 255)]
    FPhotoPath: Nullable<string>;

    [Association([TAssociationProp.Lazy], [])]
    [JoinColumn('ReportsTo', [], 'EmployeeID')]
    FReportsTo: Proxy<TEmployees>;
    function GetReportsTo: TEmployees;
    procedure SetReportsTo(const Value: TEmployees);
  public
    property EmployeeID: integer read FEmployeeID write FEmployeeID;
    property LastName: string read FLastName write FLastName;
    property FirstName: string read FFirstName write FFirstName;
    property Title: Nullable<string> read FTitle write FTitle;
    property TitleOfCourtesy: Nullable<string> read FTitleOfCourtesy write FTitleOfCourtesy;
    property BirthDate: Nullable<TDateTime> read FBirthDate write FBirthDate;
    property HireDate: Nullable<TDateTime> read FHireDate write FHireDate;
    property Address: Nullable<string> read FAddress write FAddress;
    property City: Nullable<string> read FCity write FCity;
    property Region: Nullable<string> read FRegion write FRegion;
    property PostalCode: Nullable<string> read FPostalCode write FPostalCode;
    property Country: Nullable<string> read FCountry write FCountry;
    property HomePhone: Nullable<string> read FHomePhone write FHomePhone;
    property Extension: Nullable<string> read FExtension write FExtension;
    property Photo: TBlob read FPhoto write FPhoto;
    property Notes: TBlob read FNotes write FNotes;
    property PhotoPath: Nullable<string> read FPhotoPath write FPhotoPath;
    property ReportsTo: TEmployees read GetReportsTo write SetReportsTo;
  end;

  [Entity]
  [Table('Orders')]
  [Id('FOrderID', TIdGenerator.IdentityOrSequence)]
  TOrders = class
  private
    [Column('OrderID', [TColumnProp.Required, TColumnProp.NoInsert, TColumnProp.NoUpdate])]
    FOrderID: integer;

    [Column('OrderDate', [])]
    FOrderDate: Nullable<TDateTime>;

    [Column('RequiredDate', [])]
    FRequiredDate: Nullable<TDateTime>;

    [Column('ShippedDate', [])]
    FShippedDate: Nullable<TDateTime>;

    [Column('Freight', [])]
    FFreight: Nullable<double>;

    [Column('ShipName', [], 40)]
    FShipName: Nullable<string>;

    [Column('ShipAddress', [], 60)]
    FShipAddress: Nullable<string>;

    [Column('ShipCity', [], 15)]
    FShipCity: Nullable<string>;

    [Column('ShipRegion', [], 15)]
    FShipRegion: Nullable<string>;

    [Column('ShipPostalCode', [], 10)]
    FShipPostalCode: Nullable<string>;

    [Column('ShipCountry', [], 15)]
    FShipCountry: Nullable<string>;

    [Column('EmployeeID', [])]
    FEmployeeID: Integer;

    [Association([TAssociationProp.Lazy], [])]
    [JoinColumn('ShipVia', [], 'ShipperID')]
    FShipVia: Proxy<TShippers>;
    function GetShipVia: TShippers;
    procedure SetShipVia(const Value: TShippers);
  public
    property OrderID: integer read FOrderID write FOrderID;
    property OrderDate: Nullable<TDateTime> read FOrderDate write FOrderDate;
    property RequiredDate: Nullable<TDateTime> read FRequiredDate write FRequiredDate;
    property ShippedDate: Nullable<TDateTime> read FShippedDate write FShippedDate;
    property Freight: Nullable<double> read FFreight write FFreight;
    property ShipName: Nullable<string> read FShipName write FShipName;
    property ShipAddress: Nullable<string> read FShipAddress write FShipAddress;
    property ShipCity: Nullable<string> read FShipCity write FShipCity;
    property ShipRegion: Nullable<string> read FShipRegion write FShipRegion;
    property ShipPostalCode: Nullable<string> read FShipPostalCode write FShipPostalCode;
    property ShipCountry: Nullable<string> read FShipCountry write FShipCountry;
    property EmployeeID: integer read FEmployeeID write FEmployeeID;
    property ShipVia: TShippers read GetShipVia write SetShipVia;
  end;

  [Entity]
  [Table('Shippers')]
  [Id('FShipperID', TIdGenerator.IdentityOrSequence)]
  TShippers = class
  private
    [Column('ShipperID', [TColumnProp.Required, TColumnProp.NoInsert, TColumnProp.NoUpdate])]
    FShipperID: integer;

    [Column('CompanyName', [TColumnProp.Required], 40)]
    FCompanyName: string;

    [Column('Phone', [], 24)]
    FPhone: Nullable<string>;
  public
    property ShipperID: integer read FShipperID write FShipperID;
    property CompanyName: string read FCompanyName write FCompanyName;
    property Phone: Nullable<string> read FPhone write FPhone;
  end;

  TDicDictionary = class
  private
    FEmployees: TEmployeesTableDictionary;
    FOrders: TOrdersTableDictionary;
    FShippers: TShippersTableDictionary;
    function GetEmployees: TEmployeesTableDictionary;
    function GetOrders: TOrdersTableDictionary;
    function GetShippers: TShippersTableDictionary;
  public
    destructor Destroy; override;
    property Employees: TEmployeesTableDictionary read GetEmployees;
    property Orders: TOrdersTableDictionary read GetOrders;
    property Shippers: TShippersTableDictionary read GetShippers;
  end;

  TEmployeesTableDictionary = class
  private
    FEmployeeID: TDictionaryAttribute;
    FLastName: TDictionaryAttribute;
    FFirstName: TDictionaryAttribute;
    FTitle: TDictionaryAttribute;
    FTitleOfCourtesy: TDictionaryAttribute;
    FBirthDate: TDictionaryAttribute;
    FHireDate: TDictionaryAttribute;
    FAddress: TDictionaryAttribute;
    FCity: TDictionaryAttribute;
    FRegion: TDictionaryAttribute;
    FPostalCode: TDictionaryAttribute;
    FCountry: TDictionaryAttribute;
    FHomePhone: TDictionaryAttribute;
    FExtension: TDictionaryAttribute;
    FPhoto: TDictionaryAttribute;
    FNotes: TDictionaryAttribute;
    FPhotoPath: TDictionaryAttribute;
    FReportsTo: TDictionaryAssociation;
  public
    constructor Create;
    property EmployeeID: TDictionaryAttribute read FEmployeeID;
    property LastName: TDictionaryAttribute read FLastName;
    property FirstName: TDictionaryAttribute read FFirstName;
    property Title: TDictionaryAttribute read FTitle;
    property TitleOfCourtesy: TDictionaryAttribute read FTitleOfCourtesy;
    property BirthDate: TDictionaryAttribute read FBirthDate;
    property HireDate: TDictionaryAttribute read FHireDate;
    property Address: TDictionaryAttribute read FAddress;
    property City: TDictionaryAttribute read FCity;
    property Region: TDictionaryAttribute read FRegion;
    property PostalCode: TDictionaryAttribute read FPostalCode;
    property Country: TDictionaryAttribute read FCountry;
    property HomePhone: TDictionaryAttribute read FHomePhone;
    property Extension: TDictionaryAttribute read FExtension;
    property Photo: TDictionaryAttribute read FPhoto;
    property Notes: TDictionaryAttribute read FNotes;
    property PhotoPath: TDictionaryAttribute read FPhotoPath;
    property ReportsTo: TDictionaryAssociation read FReportsTo;
  end;

  TOrdersTableDictionary = class
  private
    FOrderID: TDictionaryAttribute;
    FOrderDate: TDictionaryAttribute;
    FRequiredDate: TDictionaryAttribute;
    FShippedDate: TDictionaryAttribute;
    FFreight: TDictionaryAttribute;
    FShipName: TDictionaryAttribute;
    FShipAddress: TDictionaryAttribute;
    FShipCity: TDictionaryAttribute;
    FShipRegion: TDictionaryAttribute;
    FShipPostalCode: TDictionaryAttribute;
    FShipCountry: TDictionaryAttribute;
    FEmployeeID: TDictionaryAssociation;
    FShipVia: TDictionaryAssociation;
  public
    constructor Create;
    property OrderID: TDictionaryAttribute read FOrderID;
    property OrderDate: TDictionaryAttribute read FOrderDate;
    property RequiredDate: TDictionaryAttribute read FRequiredDate;
    property ShippedDate: TDictionaryAttribute read FShippedDate;
    property Freight: TDictionaryAttribute read FFreight;
    property ShipName: TDictionaryAttribute read FShipName;
    property ShipAddress: TDictionaryAttribute read FShipAddress;
    property ShipCity: TDictionaryAttribute read FShipCity;
    property ShipRegion: TDictionaryAttribute read FShipRegion;
    property ShipPostalCode: TDictionaryAttribute read FShipPostalCode;
    property ShipCountry: TDictionaryAttribute read FShipCountry;
    property EmployeeID: TDictionaryAssociation read FEmployeeID;
    property ShipVia: TDictionaryAssociation read FShipVia;
  end;

  TShippersTableDictionary = class
  private
    FShipperID: TDictionaryAttribute;
    FCompanyName: TDictionaryAttribute;
    FPhone: TDictionaryAttribute;
  public
    constructor Create;
    property ShipperID: TDictionaryAttribute read FShipperID;
    property CompanyName: TDictionaryAttribute read FCompanyName;
    property Phone: TDictionaryAttribute read FPhone;
  end;

function Dic: TDicDictionary;

implementation

var
  __Dic: TDicDictionary;

function Dic: TDicDictionary;
begin
  if __Dic = nil then __Dic := TDicDictionary.Create;
  result := __Dic
end;

{ TEmployees}

function TEmployees.GetReportsTo: TEmployees;
begin
  result := FReportsTo.Value;
end;

procedure TEmployees.SetReportsTo(const Value: TEmployees);
begin
  FReportsTo.Value := Value;
end;

{ TOrders}

function TOrders.GetShipVia: TShippers;
begin
  result := FShipVia.Value;
end;

procedure TOrders.SetShipVia(const Value: TShippers);
begin
  FShipVia.Value := Value;
end;

{ TDicDictionary}

destructor TDicDictionary.Destroy;
begin
  inherited;
  if FEmployees <> nil then FEmployees.Free;
  if FOrders <> nil then FOrders.Free;
  if FShippers <> nil then FShippers.Free;
end;

function TDicDictionary.GetEmployees: TEmployeesTableDictionary;
begin
  if FEmployees = nil then FEmployees := TEmployeesTableDictionary.Create;
  result := FEmployees;
end;

function TDicDictionary.GetOrders: TOrdersTableDictionary;
begin
  if FOrders = nil then FOrders := TOrdersTableDictionary.Create;
  result := FOrders;
end;

function TDicDictionary.GetShippers: TShippersTableDictionary;
begin
  if FShippers = nil then FShippers := TShippersTableDictionary.Create;
  result := FShippers;
end;

{ TEmployeesTableDictionary}

constructor TEmployeesTableDictionary.Create;
begin
  FEmployeeID := TDictionaryAttribute.Create('EmployeeID');
  FLastName := TDictionaryAttribute.Create('LastName');
  FFirstName := TDictionaryAttribute.Create('FirstName');
  FTitle := TDictionaryAttribute.Create('Title');
  FTitleOfCourtesy := TDictionaryAttribute.Create('TitleOfCourtesy');
  FBirthDate := TDictionaryAttribute.Create('BirthDate');
  FHireDate := TDictionaryAttribute.Create('HireDate');
  FAddress := TDictionaryAttribute.Create('Address');
  FCity := TDictionaryAttribute.Create('City');
  FRegion := TDictionaryAttribute.Create('Region');
  FPostalCode := TDictionaryAttribute.Create('PostalCode');
  FCountry := TDictionaryAttribute.Create('Country');
  FHomePhone := TDictionaryAttribute.Create('HomePhone');
  FExtension := TDictionaryAttribute.Create('Extension');
  FPhoto := TDictionaryAttribute.Create('Photo');
  FNotes := TDictionaryAttribute.Create('Notes');
  FPhotoPath := TDictionaryAttribute.Create('PhotoPath');
  FReportsTo := TDictionaryAssociation.Create('ReportsTo');
end;

{ TOrdersTableDictionary}

constructor TOrdersTableDictionary.Create;
begin
  FOrderID := TDictionaryAttribute.Create('OrderID');
  FOrderDate := TDictionaryAttribute.Create('OrderDate');
  FRequiredDate := TDictionaryAttribute.Create('RequiredDate');
  FShippedDate := TDictionaryAttribute.Create('ShippedDate');
  FFreight := TDictionaryAttribute.Create('Freight');
  FShipName := TDictionaryAttribute.Create('ShipName');
  FShipAddress := TDictionaryAttribute.Create('ShipAddress');
  FShipCity := TDictionaryAttribute.Create('ShipCity');
  FShipRegion := TDictionaryAttribute.Create('ShipRegion');
  FShipPostalCode := TDictionaryAttribute.Create('ShipPostalCode');
  FShipCountry := TDictionaryAttribute.Create('ShipCountry');
  FEmployeeID := TDictionaryAssociation.Create('EmployeeID');
  FShipVia := TDictionaryAssociation.Create('ShipVia');
end;

{ TShippersTableDictionary}

constructor TShippersTableDictionary.Create;
begin
  FShipperID := TDictionaryAttribute.Create('ShipperID');
  FCompanyName := TDictionaryAttribute.Create('CompanyName');
  FPhone := TDictionaryAttribute.Create('Phone');
end;

initialization

finalization
  if __Dic <> nil then __Dic.Free
{$IFEND}
end.


Queries.pas

unit Queries;

interface
{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
implementation
{$ELSE}

uses Generics.Collections, DataModel,
     Aurelius.Drivers.Interfaces, Aurelius.Drivers.dbGo,
     Aurelius.Sql.MSSQL,
     Aurelius.Engine.ObjectManager,
     ADODb,
     Aurelius.Criteria.Base, Aurelius.Criteria.Linq;

type
  TAureliusQuery = class
  private
    ADOConnection: TADOConnection;
    Connection: IDBConnection;
    Manager: TObjectManager;
    FEmployees: TObjectList<TEmployees>;
    FOrders: TObjectList<TOrders>;
  public
    constructor Create(const DBFile: string);
    destructor Destroy; override;
    function GetEmployees: TList<TEmployees>;
    function GetOrders: TList<TOrders>;

  end;

implementation
uses SysUtils;

const BAseConnectionString = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Northwind.mdb;Persist Security Info=False;';

constructor TAureliusQuery.Create(const DBFile: string);
begin
  ADOConnection := TADOConnection.Create(nil);
  ADOConnection.ConnectionString := StringReplace(BaseConnectionString, 'Northwind.mdb', DbFile, []);
  Connection := TDbGoConnectionAdapter.Create(ADOConnection, false);
  Manager := TObjectManager.Create(Connection);
end;

destructor TAureliusQuery.Destroy;
begin
  FOrders.Free;
  FEmployees.Free;
  Manager.Free;
  Connection := nil;
  ADOConnection.Free;
  inherited;
end;

function TAureliusQuery.GetEmployees: TList<TEmployees>;
begin
  FEmployees.Free;
  FEmployees := Manager.Find<TEmployees>
            .List;
  Result := FEmployees;
end;

function TAureliusQuery.GetOrders: TList<TOrders>;
begin
  FOrders.Free;
  FOrders := Manager.Find<TOrders>
            .List;
  Result := FOrders;
end;
{$IFEND}
end.

UMainForm.pas

unit UMainForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics,
  FlexCel.VCLSupport, FlexCel.Core, FlexCel.XlsAdapter, FlexCel.Report, FlexCel.Render,
  {$if CompilerVersion >= 23.0} System.UITypes, {$IFEND}
  ShellApi,
  Controls, Forms, Dialogs, StdCtrls, ExtCtrls;

type
  TMainForm = class(TForm)
    btnCancel: TButton;
    btnExportHTML: TButton;
    SaveDialogXls: TSaveDialog;
    Label1: TLabel;
    btnExportPdf: TButton;
    btnExportExcel: TButton;
    SaveDialogPdf: TSaveDialog;
    SaveDialogHtml: TSaveDialog;
    procedure btnCancelClick(Sender: TObject);
    procedure btnExportHTMLClick(Sender: TObject);
    procedure btnExportExcelClick(Sender: TObject);
    procedure btnExportPdfClick(Sender: TObject);
  private
    function RunReport(const Xls: TExcelFile; const SaveDialog: TSaveDialog): boolean;
    function GetDataPath: string;
    procedure ShowOpenResult(const SaveDialog: TSaveDialog);
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation
uses IOUtils, DataModel, Queries, AureliusFlexCelSupport;

{$R *.dfm}

function DBFile: string;
begin
  Result := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), '..\..\..\SharedData\Northwind.mdb');
end;


procedure TMainForm.btnCancelClick(Sender: TObject);
begin
  Close;
end;

procedure TMainForm.btnExportExcelClick(Sender: TObject);  
var
  Xls: TExcelFile;
begin
  Xls := TXlsFile.Create(true);
  try
    if not RunReport(Xls, SaveDialogXls) then exit;
    Xls.Save(SaveDialogXls.FileName);
    ShowOpenResult(SaveDialogXls);
  finally
    Xls.Free;
  end;
end;

procedure TMainForm.btnExportHTMLClick(Sender: TObject);
var
  Xls: TExcelFile;
  Html: TFlexCelHtmlExport;
begin
  Xls := TXlsFile.Create(true);
  try
    if not RunReport(Xls, SaveDialogHtml) then exit;
    Html := TFlexCelHtmlExport.Create(Xls, true);
    try
      Html.HtmlVersion := THtmlVersion.Html_5;
      Html.EmbedImages := true;
      Html.Export(SaveDialogHtml.FileName, '');
      ShowOpenResult(SaveDialogHtml);
    finally
      Html.Free;
    end;
  finally
    Xls.Free;
  end;
end;

procedure TMainForm.btnExportPdfClick(Sender: TObject);
var
  Xls: TExcelFile;
  Pdf: TFlexCelPdfExport;
begin
  Xls := TXlsFile.Create(true);
  try
    if not RunReport(Xls, SaveDialogPdf) then exit;
    Pdf := TFlexCelPdfExport.Create(Xls, true);
    try
      Pdf.Export(SaveDialogPdf.FileName);
      ShowOpenResult(SaveDialogPdf);
    finally
      Pdf.Free;
    end;
  finally
    Xls.Free;
  end;
end;

function TMainForm.GetDataPath: string;
begin
  Result := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), '..\..');
end;

{$IF CompilerVersion < 23.0}
//Aurelius doesn't support XE
function TMainForm.RunReport(const Xls: TExcelFile; const SaveDialog: TSaveDialog): boolean;
begin
  raise Exception.Create('Aurelius doesn''t support Delphi XE. To run this demo you need XE2 or newer');
end;
{$ELSE}
function TMainForm.RunReport(const Xls: TExcelFile; const SaveDialog: TSaveDialog): boolean;
var
  Report: TFlexCelReport;
  Query: TAureliusQuery;
begin
  if not SaveDialog.Execute then exit(false);

  Report := TFlexCelReport.Create(true);
  try
    SetupAurelius(Report);

    Query := TAureliusQuery.Create(DBFile);
    try
      Report.AddTable<TEmployees>('Employees', Query.GetEmployees, TDisposeMode.DoNotDispose);
      Report.AddTable<TOrders>('Orders', Query.GetOrders, TDisposeMode.DoNotDispose);

      //Note that we've defined EmployeeID as an integer and not a TProxy, to avoid loading
      //the full employee for every order. If we had left EmployeeID as a proxy we would have
      //to define:
      //Report.AddRelationship('Employees', 'Orders', 'EmployeeID', 'EmployeeID.EmployeeID');
      //It would work, but it would be slower.
      Report.AddRelationship('Employees', 'Orders', 'EmployeeID', 'EmployeeID');
      Report.SetValue('Date', Now);

      Xls.Open(TPath.Combine(GetDataPath, 'TMS Aurelius.template.xls'));
      Report.Run(Xls);
    finally
      Query.Free;
    end;
  finally
    Report.Free;
  end;
  Result := true;
end;
{$IFEND}

procedure TMainForm.ShowOpenResult(const SaveDialog: TSaveDialog);
begin
  if MessageDlg('Do you want to open the generated file?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin
    ShellExecute(0, 'open', PCHAR(SaveDialog.FileName), nil, nil, SW_SHOWNORMAL);
  end;
end;

end.