Search Results for

    Show / Hide Table of Contents

    Validating FlexCel recalculation (Delphi)

    Note

    This demo is available in your FlexCel installation at <FlexCel Install Folder>\Demo\Delphi\Modules\10.API\75.Validate Recalc and also at https:​//​github.​com/​tmssoftware/​TMS-​FlexCel.​VCL-​demos/​tree/​master/​Delphi/​Modules/​10.​API/​75.​Validate Recalc

    Overview

    By default, FlexCel recalculates the files it generates before saving. This is not completely necessary for normal xls/x files, because Excel will recalculate the file again when opening. But when natively printing or exporting to PDF, you need the values from the recalculation, because no Excel is involved in the process.

    While most files will recalculate fine, if you have complex formulas and need to verify they will recalculate ok, this is the application to use.

    Concepts

    • Button "Validate Recalc": This will verify that flexcel can understand all the formulas on your sheet.

    • Button "Compare with Excel": This will open a file you saved with Excel, force a recalculation in FlexCel and then compare all formula results with the original ones. You can use this to verify the results are actually what you expect them to be.

    Files

    UValidateRecalc.pas

    unit UValidateRecalc;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics,
      Controls, Forms, Dialogs, ImgList, ActnList, StdCtrls,
      Tabs, Grids,ExtCtrls, ComCtrls, ToolWin,
      FlexCel.VCLSupport, FlexCel.Core, FlexCel.XlsAdapter;
    
    type
      TFValidateRecalc = class(TForm)
        ToolBar1: TToolBar;
        ToolButton1: TToolButton;
        ToolButton2: TToolButton;
        ToolButton3: TToolButton;
        ToolButton5: TToolButton;
        ToolButton4: TToolButton;
        ToolButton6: TToolButton;
        Actions: TActionList;
        ActionValidateRecalc: TAction;
        ActionCompareWithExcel: TAction;
        ActionInfo: TAction;
        ActionClose: TAction;
        OpenDialog: TOpenDialog;
        ToolbarImages: TImageList;
        report: TMemo;
        LinkedFileDialog: TOpenDialog;
        ToolbarImages_300Scale: TImageList;
        ToolbarImages_100Scale: TImageList;
        procedure ActionCloseExecute(Sender: TObject);
        procedure ActionValidateRecalcExecute(Sender: TObject);
        procedure ActionInfoExecute(Sender: TObject);
        procedure ActionCompareWithExcelExecute(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        procedure Work_LoadLinkedFile(const sender: TObject;
          const e: TLoadLinkedFileEventArgs);
        procedure CompareXls(const xls1, xls2: TXlsFile);
        function GetErrorType(const f: TUnsupportedFormulaErrorType): string;
         { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      FValidateRecalc: TFValidateRecalc;
    
    implementation
    uses Math, IOUtils, UFlexCelHDPI;
    
    {$R *.dfm}
    
    procedure TFValidateRecalc.ActionCloseExecute(Sender: TObject);
    begin
      Close;
    end;
    
    procedure TFValidateRecalc.ActionInfoExecute(Sender: TObject);
    begin
      ShowMessage('This example will validate the calculations performed by the FlexCel engine.' +
      #$000A'It can do it in 2 different ways:'+
      #$000A'  1) The button "Validate Recalc" will analyze a file, and report if there is anything that FlexCel doesn''t support on it.' +
      #$000A'  2) The button "Compare with Excel" will open a file saved by Excel, recalculate it with FlexCel, compare the values reported by both FlexCel and Excel and report if there are any differences.');
    end;
    
    /// <summary>
    /// This event is used when there are linked files, to load them on demand.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    procedure TFValidateRecalc.Work_LoadLinkedFile(const sender: TObject; const e: TLoadLinkedFileEventArgs);
    var
      FilePath: String;
    begin
      FilePath := TPath.Combine(TPath.GetDirectoryName(OpenDialog.FileName), e.FileName);
      //IMPORTANT: DO NOT USE THIS METHOD IN PRODUCTION IF SECURITY IS IMPORTANT.
      //This method will access any file in your harddisk, as long as it is linked in the spreadhseet, and
      //that could mean an IMPORTANT SECURITY RISK. You should limit the places where the app can search for
      //linked files. Look at the "Recalculating Linked Files" in the PDF API Guide for more information.
      if TFile.Exists(FilePath) then  //If we find the path, just load the file.
      begin
        e.Xls := TXlsFile.Create;
        e.Xls.Open(FilePath);
        exit;
      end;
    
       //If we couldn't find the file, ask the user for its location.
      LinkedFileDialog.FileName := FilePath;
      if not LinkedFileDialog.Execute then  //if user cancels, e.Xls will be null, so no file will be used and an #errna error will show in the formulas.
        exit;
    
      e.Xls := TXlsFile.Create;
      try
        e.Xls.Open(LinkedFileDialog.FileName);
      except
        e.Xls.Free;
        e.Xls := nil;
        raise;
      end;
    end;
    
    
    procedure TFValidateRecalc.ActionValidateRecalcExecute(Sender: TObject);
    var
      Xls: TXlsFile;
      Work: TWorkspace;
      Usl: TUnsupportedFormulaList;
      i: Int32;
      FileName: string;
      FunctionStr: string;
    begin
      if not OpenDialog.Execute then
        exit;
    
      // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      // ////////Code here is only needed if you have linked files. In this example we don't know, so we will use it /////////
      Work := TWorkspace.Create(true);  //Create a workspace
      try
        Xls := TXlsFile.Create;
        try
          Xls.Open(OpenDialog.FileName);
        except
          FreeAndNil(Xls);
          raise;
        end;
    
        Work.Add(ExtractFileName(OpenDialog.FileName), Xls);  //Add the original file to it
        Work.LoadLinkedFile:= Work_LoadLinkedFile;  //Set up an event to load the linked files.
        // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        report.Text := 'Results on file: ' + OpenDialog.FileName;
        Usl := Work.RecalcAndVerify;
        try
          if Usl.Count = 0 then
          begin
            report.Lines.Add('**********All formulas supported!**********');
            exit;
          end;
    
          report.Lines.Add('Issues Found:');
          for i := 0 to Usl.Count - 1 do
          begin
            if Usl[i].FileName = '' then FileName := '' else FileName := ('File: ' + Usl[i].FileName) + '  => ';
            report.Lines.Add('     ' + FileName + Usl[i].Cell.CellRef + ': ' + GetErrorType(Usl[i].ErrorType));
            if Usl[i].FunctionName <> '' then
            begin
              FunctionStr := 'Function';
              if Usl[i].ErrorType = TUnsupportedFormulaErrorType.ExternalReference then
                FunctionStr := 'Linked file not found';
    
              report.Lines.Add(' ->' + FunctionStr + ': ' + Usl[i].FunctionName);
            end;
    
          end;
        finally
          Usl.Free;
        end;
      finally
        Work.Free;
      end;
    
    end;
    
    procedure TFValidateRecalc.ActionCompareWithExcelExecute(Sender: TObject);
    var
      xls1: TXlsFile;
      xls2: TXlsFile;
      Work: TWorkspace;
    begin
      if not OpenDialog.Execute then
        exit;
      Work := nil; xls1 := nil;
      try
        ActionCompareWithExcel.Enabled := false;
        ActionValidateRecalc.Enabled := false;
        try
          xls1 := TXlsFile.Create;
          xls2 := TXlsFile.Create;
          try
            xls1.Open(OpenDialog.FileName);
            xls2.Open(openDialog.FileName);
            report.Text := 'Compare with Excel: ' + OpenDialog.FileName;
    
            // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // ////////Code here is only needed if you have linked files. In this example we don't know, so we will use it /////////
            Work := TWorkspace.Create(true);  //Create a workspace
            Work.Add(TPath.GetFileName(OpenDialog.FileName), xls1);  //Add the original file to it
            Work.LoadLinkedFile:= Work_LoadLinkedFile;  //Set up an event to load the linked files.
             // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            CompareXls(xls1, xls2);
          finally
            FreeAndNil(xls2);
          end;
        finally
          ActionCompareWithExcel.Enabled := true;
          ActionValidateRecalc.Enabled := true;
        end;
      finally
        if Work <> nil then FreeAndNil(Work) else FreeAndNil(xls1);
    
      end;
    end;
    
    procedure TFValidateRecalc.CompareXls(const xls1: TXlsFile; const xls2: TXlsFile);
    var
      DiffCount: Int32;
      sheet: Int32;
      aColCount: Int32;
      r: Int32;
      c: Int32;
      f: TFormula;
      ad: TCellAddress;
      f2: TFormula;
      cell1: TCellValue;
      eps: double;
    begin
      DiffCount := 0;
      xls1.Recalc;
      for sheet := 1 to xls1.SheetCount do
      begin
        xls1.ActiveSheet := sheet;
        xls2.ActiveSheet := sheet;
        aColCount := xls1.ColCount; //ColCount is slow. We will calculate it only once here.
        for r := 1 to xls1.RowCount do
        for c := 1 to aColCount do
        begin
          cell1 := xls1.GetCellValue(r, c);
          if cell1.IsFormula then
          begin
            f := cell1.AsFormula;
            ad := TCellAddress.Create(r, c);
            f2 := xls2.GetCellValue(r, c).AsFormula;
            if f.FormulaResult = TCellValue.Empty then
              f.FormulaResult := '';
    
            if f2.FormulaResult = TCellValue.Empty then
              f2.FormulaResult := '';
    
            eps := 0;
            if f.FormulaResult.IsNumber and f2.FormulaResult.IsNumber then
            begin
              if SameValue(f2.FormulaResult.AsNumber, 0) then
              begin
                if SameValue(f.FormulaResult.AsNumber, 0) then eps := 1;
    
              end else
                eps := f.FormulaResult.AsNumber / (f2.FormulaResult.AsNumber);
    
              if Abs(eps - 1) < 0.001 then
                f.FormulaResult := f2.FormulaResult;
    
            end;
    
            if f.FormulaResult <> f2.FormulaResult then
            begin
              report.Lines.Add('Sheet:' + xls1.SheetName + ' --- Cell:' + ad.CellRef + ' --- Calculated: '
                + f.FormulaResult.ToString + '    Excel: ' + f2.FormulaResult.ToString
                + '  dif: ' + FloatToStr(eps) + '   formula: ' + f.Text);
              Application.ProcessMessages;
    
              Inc(DiffCount);
            end;
          end;
        end;
      end;
    
      report.Lines.Add('Finished Comparing.');
      if DiffCount = 0
        then report.Lines.Add('**********No differences found!**********')
        else report.Lines.Add('  --->Found ' + IntToStr(DiffCount) + ' differences');
    end;
    
    procedure TFValidateRecalc.FormCreate(Sender: TObject);
    begin
      RegisterForHDPI(Self, nil);
    end;
    
    function TFValidateRecalc.GetErrorType(
      const f: TUnsupportedFormulaErrorType): string;
    begin
      case f of
        TUnsupportedFormulaErrorType.FormulaTooComplex: exit('FormulaTooComplex');
        TUnsupportedFormulaErrorType.MissingFunction: exit('MissingFunction');
        TUnsupportedFormulaErrorType.FunctionalityNotImplemented: exit('FunctionalityNotImplemented');
        TUnsupportedFormulaErrorType.CircularReference: exit('CircularReference');
        TUnsupportedFormulaErrorType.ExternalReference: exit('ExternalReference');
      end;
      Result := IntToStr(Integer(f));
    end;
    
    end.
    
    In This Article
    Back to top FlexCel Studio for VCL and FireMonkey v7.24
    © 2002 - 2025 tmssoftware.com