Der Chatbot hilft Ihnen bei Fragen rund um das Produkt und die Anpassbarkeit der Software. Wie bei allen AI-generierten Daten sollten die Antworten bei kritischen Informationen verifiziert werden. Nehmen Sie dafür gerne Kontakt mit uns auf. Weitere Informationen zur Verarbeitung der Chat-Daten bieten wir auf der Datenschutzseite.
Die besten Antworten liefert der Chatbot, wenn Ihr Input möglichst viele Informationen enthält. Zum Beispiel:
«Welche Apps stehen im Vertec Cloud Abo zur Verfügung?»
Die Datenlogik eines erweiterten Office-Berichts basiert auf einer Hierarchie von sogenannten Frames. Ein Frame ist vergleichbar mit einer Tabelle, es hat Zeilen mit Datenfeldern drauf. Die Felder eines Frames werden mit Hilfe von Feld-Definitionen festgelegt.
Der Python Code für einen erweiterten Office-Bericht besteht im Wesentlichen aus der Deklaration der Frames, startend mit dem Haupt-Frame (main_frame) des Reports.
Eine minimale Bericht-Definition muss eine Frame-Deklaration sowie die Zuweisung der main_frame Variable enthalten. Hier wird beispielsweise ein Bearbeiter-Frame mit 2 Feldern definiert:
# Beispiel eines minimalen Mitarbeiter-Reports
class Bearbeiter(Frame):
businessclass = "Projektbearbeiter"
fields = [
OclTextField("name"),
OclTextField("kuerzel"),
]
main_frame = Bearbeiter
Jedes Band im Word verweist auf ein bestimmtes Frame (bndXXXExp). Die innerhalb des Bands verwendeten Variablen verweisen auf Felder, die im Frame deklariert wurden (siehe Abschnitt Felder weiter unten). Felder, die im Frame nicht deklariert wurden, sind im Word nicht verfügbar.
Das main_frame entspricht dem Standard-Band im Word, welches alles umschliesst.
Frames können anderen Frames als Sub-Frames zugewiesen werden. Dadurch können hierarchische Strukturen abgebildet werden:
Felder
Auf den Frames werden Felder erzeugt. Auf diese Felder kann später im Report referenziert werden. Es gibt zwei Arten von Feldern: OCL-Felder und einfache Felder. Die OCL-Felder berechnen ihren Wert über eine OCL-Expression. Bei den einfachen Feldern muss der Inhalt manuell bzw. mit einer Python Funktion abgefüllt werden.
Feld-Typen
Es gibt folgende Arten von Feldern:
Felder
OCL-Felder
Beschreibung
TextField
OclTextField
Stellt einen String-Wert dar
CurrencyField
OclCurrencyField
Stellt eine Fixkomma Zahl dar, wird üblicherweise für Geld-Beträge verwendet. Formatierung gemäss Ländereinstellung.
IntegerField
OclIntegerField
Stellt eine Ganzzahl dar
MinuteField
OclMinuteField
Stellt einen Integer-Wert als Minuten dar. Formatierung gemäss Einstellung in Vertec.
BooleanField
OclBooleanField
Stellt einen Wahr/Falsch Wert dar
DateField
OclDateField
Stellt ein Datum dar
DateTimeField
OclDateTimeField
Stellt ein Datum mit Zeitanteil dar
ImageField
OclImageField
Stellt ein Bild dar
FrameField
OclFrameField
Enthält als Wert ein weiteres Frame. Frame-Fields erlauben die Darstellung von hierarchischen Datenstrukturen.
OCL-Felder
OCL Felder enthalten als ersten Parameter den Feldnamen. Entspricht dieser dem Member, welches für die Berechnung verwendet werden soll, muss nichts weiter angegeben werden:
OclTextField("code")
Optional kann als zweites Argument eine OCL Expression angegeben werden:
OclTextField("name", "projektleiter.name")
OCL-Felder sind nur auf Frames zulässig, welche einer eindeutigen Businessklasse zugeordnet werden können. Diese wird in der Deklaration des Frames mittels businessclass= angegeben.
Einfache Felder
Einfache Felder (ohne OCL-Präfix) enthalten ebenfalls als ersten Parameter den Feldnamen. Ohne weitere Angaben stellen diese nicht automatisch berechnete Felder dar, welche beim Berechnen des Frames im Code mit Werten gefüllt werden können.
CurrencyField("value1")
Optional kann als zweites Argument eine Python Funktion angegeben werden, die zur Berechnung des Feldes aufgerufen wird. Beispiel:
class Rechnnung(Frame):
fields = [CurrencyField("summe", "calc_summe")]
def calc_summe(context):
rechnung = context.currentobject
sum = 0.0
for leist in rechnung.leistungen:
sum += leist.wertext
return sum
Frame-Felder (Subframes)
Etwas anders als die anderen Feld-Typen sind die Frame-Felder aufgebaut, also die Felder, mit welchen die Sub-Frames angegeben werden.
Name des Feldes: Das erste Argument enthält wie immer den Namen, mit welchem im Report auf dieses Subset zugegriffen werden kann, beispielsweise als Band-Expression.
Name des Frames: Mit dem zweiten Argument wird die entsprechende Frame-Deklaration übergeben:
OCL-Expression: Mit dem dritten Argument wird angegeben, wie der Inhalt des Sub-Frames berechnet wird.
Falls die Berechnung per OCL Expression erfolgt (OclFrameField), muss das Ergebnis der Expression eine Liste von Businessobjekten sein, welche der Frame Deklaration entsprechen.
Falls die OCL Expression dem Namen des Frames entspricht, so kann diese weggelassen werden. Der Report Mechanismus wird dann den Namen als OCL Expression verwenden:
OclFrameField("offeneLeistungen", "Leistung")
Falls die Berechnung per Python Funktion erfolgt (FrameField), wie im obigen Beispiel dargestellt, muss die Funktion eine Frame-Instanz des richtigen Typs zurückgeben.
Frames werden wie folgt aufgebaut:
Frames
Ein Frame deklarieren
Syntax:
Class XY(Frame):
Das Haupt-Frame zuweisen (main_frame)
Es gibt ein Haupt-Frame. Dieses heisst main_frame und muss ein deklariertes Frame zugewiesen bekommen:
main_frame = XY
Sub-Frames zuweisen (FrameField)
Frames können anderen Frames als Sub-Frames zugewiesen werden. Dies geschieht mit einem Frame-Feld (siehe Abschnitt Felder weiter oben).
Frames können auch im Code aufgebaut werden. Dies wird dann benötigt, wenn in einer Zeile, also in einem Band, verschiedene Objekte dargestellt werden sollen, welche nicht zur gleichen Businessklasse gehören, und also nicht automatisch berechnet werden können.
Dafür steht auf dem Frame Objekt eine add_row Methode zur Verfügung, welche ein zeilenweises Aufbauen des Frames erlaubt. Die auf dem Frame definierten Felder stehen als Eigenschaften des Row-Objektes zur Verfügung.
class DetailsFrame(Frame):
fields = [TextField("code"),
CurrencyField("value1"),
CurrencyField("value2"),
]
class Invoice(Frame):
fields = [FrameField("details", DetailsFrame, calculate_details)]
Die einzelnen Zeilen eines solchen Frames werden via add_row() hinzugefügt. Bei der Berechnung werden dann die Felder des Frames gefüllt.
Alle Frames ausser dem main_frame werden als Feld in einem anderen Frame angegeben und dabei eine Berechnung mitgegeben.
Beim allerersten Frame, dem main_frame, ist das nicht möglich. Möchte man dieses erste Frame berechnen, kann dies mit der Methode calculate_main_frame geschehen.
Dies braucht man immer dann, wenn man im main_frame etwas berechnen möchte (und nicht beispielsweise alle Projekte bzw. das Objekt, auf welchem man den Report startet) haben möchte.
Die Methode calculate_main_frame gibt es in 2 Varianten:
calculate_main_frame ist eine Funktion calculate_main_frame ist eine Funktion, welche ein Context-Objekt erhält und eine Frame-Instanz des richtigen Typs zurückgeben soll.Signatur der Funktion ist calculate_main_frame(context), Rückgabewert ist eine Frame Instanz.Beispiel:Der Report ist auf einer Liste von Projekten registriert, das Main-Frame stellt aber eine Liste von Bearbeitern dar. Die Main-Frame Klasse heisst Bearbeiter.
main_frame=Bearbeiter
def calculate_main_frame(context):
bearbeiter_list = context.evalocl("bearbeiter")
frame = Bearbeiter(context)
for b in bearbeiter_list:
row = frame.add_row(b)
row.specialvalue = calculate_some_value()
return frame
calculate_main_frame ist ein String calculate_main_frame ist ein String. Dieser wird als OCL Expression ausgewertet und muss eine Liste ergeben. Die Expression wird auf der ursprünglichen Liste des Reports ausgewertet.Beispiel, wie oben, aber nur mit Expression:
Funktionen können innerhalb eines Frames zugewiesen werden und sind dann in diesem Frame verfügbar. Sollen Funktionen überall verfügbar sein, müssen Sie ausserhalb der Frames deklariert werden.
Jeder Berechnung im Rahmen der Report-Generierung wird das Context-Objekt mitgegeben. Dieses wird so durch alle Berechnungsfunktionen durchgeschlauft und hat folgende Eigenschaften:
Das Context-Objekt
Variablen
Die folgenden Variablen sind in jedem Report definiert:
currentobject
Das jeweils aktuelle Objekt der Berechnung.
context.currentobject
currentdate
Das heutige Datum, ohne Zeitteil. context.currentdate
optarg
Das optionale Adressen-Argument bei Berichten. Um mit dem Objekt weiterzuarbeiten, kann wie folgt darauf zugegriffen werden:
class Projekt(Frame): fields = [OclTextField("code"), OclTextField("beschrieb"), OclTextField("creationdatetime"), TextField('adresstext', 'calcadresse'), ]
Die Liste, auf der der Report ausgeführt wurde. Normalerweise ist das die eintraege-Liste des Containers, auf dem der Report ausgeführt wurde, beziehungsweise eine Liste mit dem Objekt drin, auf dem der Report ausgeführt wurde.
container
Der Container, auf dem der Report ausgeführt wurde.
var<frame>
Für jedes übergeordnete Frame ist eine Variable mit dessen aktuellem Objekt definiert.
var<frame>List
Für jedes übergeordnete Frame ist eine Variable mit der Liste der Objekte des Frames definiert.
Weitere Variablen können selbst definiert und dem Context-Objekt zugewiesen werden.
firma = vtcapp.getpropertyvalue('Firma')
if firma:
context.firma = firma
Die Context-Variablen können später auf dem Report direkt über eine Context-Expression ausgegeben werden.
Bilder als Context-Variablen definieren
Ab Vertec 6.5.0.15 kann via set_image(name, value)ein Bild in eine Context-Variable eingelesen werden:
logo = vtcapp.getpropertyvalue('CompanyLogo')
context.set_image('logo', logo)
Ausserdem verfügt das Context-Objekt über die Methode evalocl(<expression>), welche eine OCL-Expression auf dem aktuellen Objekt (currentobject) auswertet.
Wenn das letzte Argument von context.evalocl None ist oder nicht angegeben wird, dann wird das currentobject des contexts verwendet. Man kann aber auch ein Objekt oder eine Liste als Argument übergeben; in diesem Fall wird die Expression auf das übergebene Objekt bzw. Liste angewendet.
Namensgebung: Die Namen der automatisch gesetzten Variablen sind gleich wie auf dem Contextobjekt, nämlich varXY und varXYList. Die manuell auf dem Contextobjekt gesetzten Variablen können mit Keywords in OCL kollidieren (self, date, now). In diesem Fall erscheint eine Fehlermeldung.
Die Reporting OCL Variablen werden auf dem privaten OCL Evaluator des ausgeführten Reports angelegt. Das bedeutet, dass die Variablen nur in OCL Expressions von OCL-Feldern oder in der context.evalocl Methode verwendet werden können. Alle anderen OCL Aufrufe, insbesondere die evalocl Methoden von Businessobjekten, verwenden den globalen OCL Evaluator und der kennt die lokalen Report-Variablen nicht. Statt z.B. spesenlist.evalocl(..) wird das wie folgt geschrieben:
Es werden nur OCL kompatible Werte als Variablen unterstützt (Vertec-Objekte und -Listen, einfache Werte). Bei inkompatiblen Werten (z.B. Python Dictionaries, Python-Listen, etc) erscheint ein Fehler.
Zugriff auf andere Felder in Feldberechnung
Ab Vertec 6.2.0.8 kann in in der Berechnungsmethode eines Fields auf andere Fields des selben Frames zugegriffen werden. Dadurch müssen Berechnungen nur noch einmal durchgeführt und danach kann direkt mit dem Wert weitergearbeitet werden. Das ist nicht nur übersichtlicher, sondern auch ein Performance-Vorteil.
Dafür gibt auf dem Context eine Methode context.get_fieldvalue(fieldname).
Um vor der Ausführung des Reports Python Code ausführen zu können (um beispielsweise Dialoge anzuzeigen und Parameter auf dem Context-Objekt zu setzen), gibt es die Methode before_report(context):
def before_report(context):
"""Frage den User nach dem Datum ab."""
initValues = {}
initValues["Stichdatum"] = vtcapp.currentdate()
dlgDefinition="""
<Dialog Title="{Translate 'Choose date'}" Width="400">
<Group Orientation="Vertical">
<DatePicker Name="Stichdatum" Label="Stichdatum" />
</Group>
<Dialog.Buttons>
<Button Text="OK" IsAccept="True" Command="{Binding OkCommand}" />
<Button Text="Cancel" IsCancel="True" Command="{Binding CancelCommand}" />
</Dialog.Buttons>
</Dialog>
"""
ok, values = vtcapp.showcustomdialog(dlgDefinition, initValues)
if not ok:
return False
context.stichdatum = values["Stichdatum"]
Referenzen
Referenzen auf Frames und Funktionen können auf zwei Arten angegeben werden:
Referenz als Name: Wird der Referenzname in Anführungszeichen geschrieben, handelt es sich um eine Referenz als Name. In diesem Fall kann die referenzierte Deklaration irgendwo im Code stehen, die Reihenfolge spielt keine Rolle.
Direkte Referenz: Wird der Referenzname ohne Anführungszeichen geschrieben, handelt es sich um eine direkte Referenz. In diesem Fall muss die referenzierte Deklaration oberhalb im Code stehen, also bevor darauf referenziert wird.
Die Referenz als Name wurde eingeführt, weil es zu leserlicherem Code führt. So sieht man zuerst, wo und warum etwas referenziert wird, und dann, was es ist.
Zusammenspiel Frames – Bands (Word)
Jedes Band im Word verweist auf ein bestimmtes Frame. Die innerhalb des Bands verwendeten Variablen verweisen auf Felder, die im Frame deklariert wurden. Felder, die im Frame nicht deklariert wurden, sind im Word nicht als Variablen verfügbar.
Einzelreports
Bei Einzelreports wird für das aktuelle Objekt ein Report erzeugt. Hier ist das main_frame das einzelne Objekt.
Listenreports
Bei Listenreports gibt es zwei Varianten:
Für jedes Objekt in der Liste, z.B. für jeden Bearbeiter, soll eine neue Seite im Report erscheinen. In diesem Fall ist der Report gleich aufgebaut wie der Einzelreport.Hier ist das main_frame einfach die Liste:
Die Liste soll als Liste im Report erscheinen, rundherum gibt es aber noch einen Rahmen wie z.B. einen Übertitel oder Texte, welche übersetzt werden sollen, einen Stichtag etc. In diesem Fall braucht es ein Rahmenframe, welches genau einmal aufgerufen wird (und nicht einmal pro Objekt in der Liste). Dies kann wie folgt geschehen:Zuerst wird ein Frame erzeugt, welches als Sub-Frame die eigentliche Liste darstellt (welche im ersten Beispiel das main_frame wäre):
Dann wird dieses extra erstellte Frame als main_frame zugewiesen. Da dieses Frame kein Objekt hat, welches dargestellt werden kann, muss via add_row eine einzelne Zeile erzeugt werden – sonst erscheint eine Fehlermeldung.
Für die Generierung von ZUGFeRD Metadaten in Rechnungsreports gibt es ab Vertec Version 6.3.0.12 die Methode
metadata_zugferd(context)
Wird diese Methode im Report-Code definiert, werden die damit generierten XML-Metadaten in das PDF des Reports integriert. Das genaue Vorgehen ist im separaten Artikel Rechnungen nach ZUGFeRD 2.0 Standard (X-Rechnung) beschrieben.