Home General
New Blog Posts: Merging Reports - Part 1 and Part 2

TDBChart printed in automated background task

We have a command line program that runs via a Scheduled Task to automatically generate reports on a schedule.

These reports are made by end users in a different program. They write their own SQL and make their own RTMs (which are both then stored in our database).

When the command line program runs, it dynamically creates a TppReport, TppDBPipeline, TDataSource, and T*Qry, hooks them all up and generates the report.

This is all working fine.

Now our users have expressed a desire to put a TDBChart on their reports. At first I thought no problem, just add ppChrtDB, ppChrt, ppCTDsgn, to the uses clause. This lets it load up a report without error, but the data never gets hooked to the chart. The data can be shown via normal TppDBText and whatnot, but never to the chart.

I tried giving a username to the dynamically created TppDBPipeline that matches exactly the Username of the pipeline that was used to make the report. For example if the TDBChart looked like below, I gave the dynamically created pipeline the name "plQry":
object ppDPTeeChart1: TppDPTeeChart
      DesignLayer = ppDesignLayer1
      UserName = 'DPTeeChart1'
      Border.mmPadding = 0
      mmHeight = 52917
      mmLeft = 2381
      mmTop = 1588
      mmWidth = 182034
      BandType = 0
      LayerName = Foreground
      object ppDPTeeChartControl1: TppDPTeeChartControl
        Left = 0
        Top = 0
        Width = 400
        Height = 250
        BackWall.Color = clWhite
        Title.Text.Strings = (
          'Chart')
        BackColor = clWhite
        MaxPointsPerPage = 0
        Page = 1
        ScaleLastPage = True
        BevelOuter = bvNone
        BorderStyle = bsNone
        Color = clWhite
        DefaultCanvas = 'TTeeCanvas3D'
        ColorPaletteIndex = 13
        object Series1: TBarSeries
          DataSource = plQry
          XLabelsSource = 'TYPE'
          XValues.Name = 'X'
          XValues.Order = loAscending
          XValues.ValueSource = 'TheNumber'
          YValues.Name = 'Bar'
          YValues.Order = loNone
          YValues.ValueSource = 'TheNumber'
        end
      end
    end
  end

Here's a minimal sample of the scheduled task:
FReportQry := TAdsQuery.Create(FConn);
      FReportQry.AdsConnection := FConn;
      FReportQry.SQL.Add(WorkingOnThisReport.SQL);
      FReportQry.Open;

      FDS := TDataSource.Create(FReportQry);
      FDS.DataSet := FReportQry;

      plQry := TppDBPipeline.Create(FDS);
      plQry.UserName := 'plQry';
      plQry.DataSource := FDS;     

      FReport := TppReport.Create(nil);
      try
        FReport.Template.SaveTo := stDatabase;
        FReport.Template.DatabaseSettings.DataPipeline := PLReports;
        FReport.Template.DatabaseSettings.NameField := 'ReportName';
        FReport.Template.DatabaseSettings.TemplateField := 'Report';
        FReport.Template.DatabaseSettings.Name := WorkingOnThisReport.ReportName;
        try
          FReport.Template.LoadFromDatabase;
        except
          on E: Exception do
          begin
            Logging.DoLog('Exception loading the RTM: ' + E.Message, Preferences.LoggingMechanism);
          end;
        end;

        FReport.DataPipeline := plQry;               

        FReport.ShowPrintDialog := False;
        FReport.ShowAutoSearchDialog := False;
        FReport.ShowCancelDialog := False;
        FReport.TextFileName := WorkingOnThisReport.FullReportPathAndName;
        FReport.DeviceType := WorkingOnThisReport.DeviceType; 
        FReport.Print;  
      finally
        FReport.Free;
      end;		

Comments

  • Hi Dusten,

    My guess is that the connection to the chart is being lost at some point (likely as the template is loaded but no pipeline is assigned). I suggest re-assigning the Series.Datasource property after assigning the Report.DataPipeline property and see if that resolves the problem.

    You could also try tracing into the TppDPTeeChartControl.RefreshData; routine to see if the pipeline is correct. (ppChrtDP.pas)
    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited April 22
    The Scheduled Task does not know if a given report has a Series/Chart on it or not. Some reports might be simple lists, others might have a couple charts, and even others just a group summary.

    Is there a way I can iterate over the report components after it has been loaded from the template and detect when a chart exists?

    Another thought would be to use RAP/Calc Tab to assign the Series.DataSource, but I can't find a way to access the Series in RAP.
  • Hi Dusten,

    Looking closer at your code, it appears you are assigning the owner of your dynamic objects incorrectly.

    TAdsQuery.Create(FConn);
    TDataSource.Create(FReportQry);
    TppDBPipeline.Create(FDS);
    TppReport.Create(nil);

    The owner of these components would typically be the same object (a form or a data module). Start by changing this and see if it helps the issue.

    If not, take a look at a report object loop to find report components.

    https://rbwiki.digital-metaphors.com/delphi-code/layouts-delphi-code/report-object-loop/
    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • Initial tests are looking good. Thanks for the tip Nico. Here's what we landed on. We had to make some "rules" for creating the charts for the end-users as we could only rebuild them in a specific way.
    
            for liBand := 0 to FReport.BandCount-1 do
            begin
              for liObject := 0 to FReport.Bands[liBand].ObjectCount-1 do
              begin
                lObject := FReport.Bands[liBand].Objects[liObject];
    
                //would need to put this in it's own procedure and recursively call it if we used subreports
                if (lObject is TppDPTeeChart) then
                begin
                  for liSeries := 0 to (lObject AS TppDPTeeChart).Chart.SeriesCount - 1 do
                  begin
                    lSeries := (lObject AS TppDPTeeChart).Chart.Series[liSeries];
    
                    if lSeries.FunctionType = nil then
                      lSeries.DataSource := PLUserDefinedExport
                    else
                      lSeries.DataSource := (lObject as TppDPTeeChart).Chart.Series[0];
                  end;
                end;
              end;
            end;
Sign In or Register to comment.