Seitenanzahl auf Reports (Seite X von Y)

2. Februar 2009 16:56

Leider gibt es in Navision (zumindest bis zur Version 5.x) keine Funktion um die Gesamtanzahl der Seiten in einem Report zu ermitteln.
Es gibt jedoch einen Workaround um die Gesamtseitenanzahl zu bekommen:

Das Prinzip funktioniert so: Wie lassen den Report laufen ohne Ausgabe zu erzeugen indem wir ihn per SAVEASHTML laufen lassen. Dann kennen wir die Seitenanzahl und können diese bei einem zweiten Durchgang des Reports (bei dem dann auch Ausgabe erzeugt wird) verwenden. Die Ausgabe des Reports dauert dadurch leider etwas länger, da der Report zweimal ausgeführt wird und man muss dies für alle Reports, auf denen man die Gesamtseitenzahl haben will, einzeln implementieren.

Anmerkung zur Syntax: Wenn im Code ein Ausdruck in spitzen Klammern erscheint, dann muss dieser Ausdruck für den jeweiligen Report ersetzt werden. Was einzusetzen ist habe ich im Anschluss an den Code-Block beschrieben.

So geht's:
  1. Report im Designer öffnen
  2. zwei globale Variablen erstellen:
    Code:
    Name            | DataType
    ----------------+---------
    CountTotalPages | Boolean
    TotalNoOfPages  | Integer

  3. Drei globale Funktionen erstellen:

    SetCountTotalPages()
    Code:
    CountTotalPages := TRUE;

    GetTotalNoOfPages() : Integer
    Code:
    EXIT(CurrReport.PAGENO);

    SetReqFormParams(<Nötige Request Form Parameter>)
    Code:
    <Entsprechende Zuweisungen>
    Die Funktion SetReqFormParams ist nötig um Parameter, die normalerweise auf dem Request Form angegeben werden an den Report zu übergeben, der die Seiten zählt. Hierbei brauch man allerdings nur Parameter beachten, die Auswirkungen auf die Seitenanzahl haben könnten. Z.B. brauch man beim Report 205 den Parameter ShowInternalInfo da durch ihn die Dimensionen auf den Report kommen. Den Parameter NoOfCopies benötigt man allerdings nicht, da dieser keine Auswirkungen auf die Anzahl der Seiten des Dokuments hat (sondern es werden einfach nur mehrere Kopien des Dokuments erstellt). Also konkret für Report 205:

    SetReqFormParams(InternalInfo : Boolean)
    Code:
    ShowInternalInfo := InternalInfo;

  4. Report Designer schließen und erneut öffnen (dies ist notwendig, damit die neuen Funktionen bekannt werden; bei mir gab es sonst immer einen Fehler, dass die Funktionen nicht existieren)

  5. Jetzt müssen wir den Code für die eigentliche Zählung einfügen. Wo der Code einzufügen ist (also in welchem Trigger) hängt davon ab, wie die Zählung erfolgen soll. Ich versuche das an zwei Beispielen zu verdeutlichen:

    Beispiel 1: Report 205 - Order Confirmation (Verkauf - Auftragsbestätigung)
    Da man unter Umständen mehrere Aufträge auf einmal ausdrucken möchte, die Aufträge aber unterschiedlich lang sein können, muss man die Seitenzahl für jeden Auftrag einzeln zählen. Deshalb muss man den Code im OnAfterGetRecord() Trigger des "Sales Header" einfügen.

    Beispiel 2: Report 11002 - G/L Total-Balance (Sachkonto - Summen Saldenliste)
    Dieser Report läuft über viele Sachkonten, aber die Zählung der Seitenzahl soll für den gesamten Report gelten (wir drucken genau eine Summen-Saldenliste und nicht wie oben mehrere Aufträge). Deshalb muss man in diesem Fall den Code in den OnPreReport() Trigger einfügen.

    Der Code muss also an die Stelle, an der die Zählung von vorne beginnen soll (in unseren Beispielen also bei jeder neuen Bestellung bzw. nur am Anfang des Reports).
    Als das "RootDataItem" bezeichne ich im Folgenden das zentrale DataItem des Reports, also die Source Table des Reports. Für den Report 205 ist dies "Sales Header", für den Report 11002 ist es "G/L Account" usw..

    Hier jetzt also der Code, den man einfügen muss:
    Code:
    // Reset current page number
    CurrReport.PAGENO := 1;

    // Get total number of pages
    IF NOT CountTotalPages THEN
    BEGIN
      RootDataItem := <Bezeichner des RootDataItems>;
      <Filter setzen>
      PageCountReport.SETTABLEVIEW(RootDataItem);
      PageCountReport.SetCountTotalPages;
      PageCountReport.SetReqFormParams(<Nötige Request Form Parameter>);

      Path := DELCHR(ENVIRON('TEMP'),'>','\') + '\' + FORMAT(CREATEGUID);
      CREATE(FileSystem);
      FileSystem.CreateFolder(Path);

      IF PageCountReport.SAVEASHTML(Path + '\' + DELCHR(PageCountReport.OBJECTID) + '.html') THEN
      BEGIN
        TotalNoOfPages := PageCountReport.GetTotalNoOfPages;
        CLEAR(PageCountReport);
        FileSystem.DeleteFolder(Path);
      END;
      CLEAR(FileSystem);
    END;
    // End of Get total number of pages

    <Bezeichner des RootDataItems> und <Nötige Req Form Parameter> sind entsprechend einzusetzen. Z.B. für den Report 205: <Nötige Req Form Parameter> = ShowInternalInfo; Wenn das oberste DataItem "Sales Header" heißt, dann <Bezeichner des RootDataItems> = "Sales Header", wenn es SalesHeader heißt, dann <Bezeichner des RootDataItems> = SalesHeader, etc.

    Die Zeile <Filter setzen> hängt wieder davon ab wie gezählt werden soll. Man muss hier den Filter für RootDataItem so setzen, dass nur der Teil der Daten zurückgeliert wird, der in diesem Durchlauf gezählt werden soll. Für unsere beiden Beispiele von oben bedeutet das:

    Beispiel 1: Hier wollen wir nur die Seiten des aktuellen Auftrags zählen. Deshalb darf der Filter von RootDataItem nur genau diesen Auftrag zurück liefern:
    <Filter setzen> = RootDataItem.SETRECFILTER;

    Beispiel 2: Da die Zählung hier über den gesamten Report läuft können wir den Filter einfach übernehmen:
    <Filter setzen> = RootDataItem.SETVIEW("G/L Account".GETVIEW);

    Also nochmal konkret für Report 205:
    Code:
    // Reset current page number
    CurrReport.PAGENO := 1;

    // Get total number of pages
    IF NOT CountTotalPages THEN
    BEGIN
      RootDataItem := "Sales Header";
      RootDataItem.SETRECFILTER;
      PageCountReport.SETTABLEVIEW(RootDataItem);
      PageCountReport.SetCountTotalPages;
      PageCountReport.SetReqFormParams(ShowInternalInfo);

      Path := DELCHR(ENVIRON('TEMP'),'>','\') + '\' + FORMAT(CREATEGUID);
      CREATE(FileSystem);
      FileSystem.CreateFolder(Path);

      IF PageCountReport.SAVEASHTML(Path + '\' + DELCHR(PageCountReport.OBJECTID) + '.html') THEN
      BEGIN
        TotalNoOfPages := PageCountReport.GetTotalNoOfPages;
        CLEAR(PageCountReport);
        FileSystem.DeleteFolder(Path);
      END;
      CLEAR(FileSystem);
    END;
    // End of Get total number of pages

    Außerdem muss man im selben Trigger noch folgende lokale Variablen anlegen:
    Code:
    Name            | DataType   | Subtype                                             | Length
    ----------------+------------+-----------------------------------------------------+-------
    Path            | Text       |                                                     | 250
    RootDataItem    | Record     | <Typ des RootDataItems>                             |
    PageCountReport | Report     | <Typ des Reports>                                   |
    FileSystem      | Automation | 'Windows Script Host Object Model'.FileSystemObject |
    <Typ des RootDataItems> und <Typ des Reports> sind entsprechend einzusetzen.
    Z.B. für den Report 205: <Typ des RootDataItems> = "Sales Header" und <Typ des Reports> = "Order Confirmation"

Die globale Variable TotalNoOfPages enthält dann die Anzahl der Seiten für das jeweilige RootDataItem (also im Falle von Report 205 die Anzahl der Seiten der jeweiligen Auftragsbestätigung).
Die temporären Dateien, die während dem Erzeugen der HTML Ausgabe entstehen, sind hinterher wieder gelöscht.



Zu diesem Thema gab es auch eine Diskussion: http://www.msdynamics.de/viewtopic.php?f=8&t=1860&p=4124
Dies hier ist eine Zusammenfassung mit Verbesserung dessen, was dort entwickelt wurde.
Zuletzt geändert von Ignitor am 16. Juni 2009 13:35, insgesamt 5-mal geändert.

Re: Seitenanzahl auf Reports (Seite X von Y)

3. Februar 2009 15:04

Update:
  • Formatierung etwas geändert
  • Abschnitte mit "<...>" Variablen konkret für Report 205 angegeben
  • Funktion SetReqFormParams ergänzt um korrekte Seitenanzahl sicher zu stellen

Re: Seitenanzahl auf Reports (Seite X von Y)

4. Februar 2009 16:19

Update:
  • CurrReport.PAGENO := 1; ergänzt um die aktuelle Seitenzahl bei jedem RootDataItem zurück zu setzen. Sonst erhält man Angaben wie "Seite 5 von 2" wenn man mehrere RootDataItems auf einmal druckt. Man kann sich das CurrReport.PAGENO := 1; allerdings auch sparen, wenn in dem Report eine CopyLoop drin ist, die CurrReport.PAGENO bereits zurücksetzt (wie z.B. bei Report 205).

Re: Seitenanzahl auf Reports (Seite X von Y)

16. Juni 2009 12:02

Update:
  • Schritt 5 & 6 zusammengefasst und überarbeitet um Anleitung auf alle Reports zu verallgemeinern (Reports, die strukturell stark von Reports wie 205,206,405, etc. abweichen werden jetzt auch abgedeckt; wie z.B. Report 11002)

Re: Seitenanzahl auf Reports (Seite X von Y)

10. März 2010 14:23

Super!

Vielen Dank für die Beschreibung!