Aktionen
Im Modell von time cockpit können Sie Aktionen definieren. Aktionen lassen sich für Workflows, zum Erstellen von Rechnungen usw. verwenden. Generell können Sie Aktionen immer dann einsetzen, wenn Sie ein bestimmtes Verhalten umsetzen möchten, das time cockpit nicht standardmäßig mitbringt.
Aufbau von Aktionen
Aktionen werden als Instanzen der Klasse TimeCockpit.Data.DataModel.Actions.ModelAction dargestellt. Über die Eigenschaft ModelActions können Sie dem Modell eine Aktion hinzufügen.
Sie können Bedingungen definieren, die erfüllt sein müssen, damit eine Aktion ausgeführt werden kann. Jede Bedingung wird durch eine Instanz der Klasse TimeCockpit.Data.DataModel.Condition oder einer ihrer abgeleiteten Klassen dargestellt. Die wichtigste Bedingung ist TimeCockpit.Data.DataModel.Actions.ModelEntityTypeCondition. Damit legen Sie fest, dass eine bestimmte Aktion nur auf Instanzen eines bestimmten Entitätstyps angewendet werden kann.
Eine Aktion kann einen optionalen Parameter haben. Der Parameter wird durch eine Instanz der Klasse TimeCockpit.Data.DataModel.Actions.ModelActionParameterDefinition oder einer ihrer abgeleiteten Klassen dargestellt. Mit einem Parameter können Sie vom Benutzer zusätzliche Daten abfragen, die zum Ausführen der jeweiligen Aktion nötig sind.
Nicht zuletzt muss jede Aktion ein Binding haben. Das Binding legt fest, woher der Code der Aktion stammt. Derzeit unterstützt time cockpit nur Aktionen, die in Python geschrieben sind. Daher müssen Sie TimeCockpit.Data.DataModel.Actions.IronPythonBinding verwenden.
Aktionen erstellen und aktualisieren
Das folgende Beispiel erstellt eine Aktion aus einer Python-Quelldatei:
clr.AddReference("System")
from System.IO import File
model = Context.GetWritableModel()
changeTimesheetAction = ModelAction()
changeTimesheetAction.Name = "CreateInvoice"
changeTimesheetAction.Conditions.Add(ModelEntityTypeCondition({ "Name": "TimesheetCondition", "ModelEntityName": "Timesheet", "ModelEntity": model.Timesheet, "MinimumInputSetSize": 1 }))
changeTimesheetAction.Binding = IronPythonBinding()
changeTimesheetAction.Binding.Name = "IronPythonBinding"
changeTimesheetAction.Binding.SourceCode = File.ReadAllText("C:\Data\Documents\Action for creating invoices.py")
model.Actions.Add(changeTimesheetAction)
Context.SaveModel(model)
print "Done"
Das folgende Beispiel aktualisiert eine Aktion aus einer Python-Quelldatei. Es fügt der Aktion eine Filterentität hinzu, die angezeigt wird, bevor die Aktion ausgeführt wird.
clr.AddReference("System")
from System.IO import File
model = Context.GetWritableModel()
filterEntity = ModelEntity()
filterEntity.Name = "FilterEntityForCreateInvoice"
filterEntity.Properties.Add(TextProperty({ "Name": "InvoiceText", "MaxStorageSize": 200, "InvariantFriendlyName": "Rechnungstext" }))
filterEntity.Properties.Add(DateTimeProperty({ "Name": "InvoiceDate", "FractionalSecondsPrecision": 0, "IsNullable": True, "InvariantFriendlyName":"Rechnungsdatum" }))
filterEntity.Relations.Add(Relation({ "Name": "Customer", "InvariantFriendlyName": "Kunde", "Target": model.Customer }))
model.Actions.CreateInvoice.Parameter = PredefinedAnonymousParameter()
model.Actions.CreateInvoice.Parameter.ModelEntity = filterEntity
model.Actions.CreateInvoice.Binding.SourceCode = File.ReadAllText("C:\Data\Documents\Action for creating invoices.py")
Context.SaveModel(model)
print "Done"
Code einer Aktion
Das folgende Beispiel zeigt den Code einer Aktion. Beachten Sie, dass der Code einer Aktion ausschließlich aus Python-Methodendefinitionen bestehen darf. Die Aktion erhält einen Parameter vom Typ TimeCockpit.Data.DataModel.Actions.ExecutionContext.
clr.AddReference("System")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Core")
from System import DateTime
from System import Decimal
from System.Windows import MessageBox
from System.Linq import Enumerable
from TimeCockpit.Data import EntityObject
def createInvoice(actionContext):
# make sure that input set contains at least one object
if (Enumerable.Count[EntityObject](actionContext.InputSet) > 0):
# find next free invoice number
lastInvoiceNumber = 0
lastInvoice = actionContext.DataContext.SelectSingleWithParams( {
"Query": "From I In Invoice Where :Year(I.InvoiceDate) = @YearParam And :Month(I.InvoiceDate) = @MonthParam Order By I.InvoiceNumberMonthly Desc Select New With { I.InvoiceNumberMonthly }",
"@YearParam": Decimal(actionContext.Parameter.InvoiceDate.Year),
"@MonthParam": Decimal(actionContext.Parameter.InvoiceDate.Month) })
if (lastInvoice <> None):
lastInvoiceNumber = lastInvoice.InvoiceNumberMonthly
# create invoice
newInvoice = actionContext.DataContext.CreateInvoice()
newInvoice.InvoiceDate = actionContext.Parameter.InvoiceDate
newInvoice.InvoiceNumberMonthly = lastInvoiceNumber + 1
newInvoice.InvoiceText = actionContext.Parameter.InvoiceText
newInvoice.Customer = actionContext.Parameter.Customer
actionContext.DataContext.SaveObject(newInvoice)
Das InputSet verwenden
Bevor eine Aktion ausgeführt wird, lädt time cockpit die betroffenen Datenzeilen neu, bevor es sie über die Eigenschaft InputSet an die Aktion übergibt. Das ist nötig, damit die vollständigen Zeilen einschließlich aller abhängigen Objekte im Speicher sind. Eine Liste könnte so optimiert sein, dass sie nur einen Teil der Spalten einer Modellentität lädt. Würde time cockpit die Datenzeilen vor dem Ausführen der Aktion nicht neu laden, bekäme die Aktion unter Umständen unvollständige Daten.
Warnung
Beachten Sie, dass das InputSet keine definierte Reihenfolge der Objekte garantiert. Je nach Anwendungsfall kann eine Sortierung nötig sein.
Zum Neuladen der Entitäten erzeugt time cockpit eine TCQL-Anweisung mit folgendem Aufbau: From C In [ModelEntityName][IncludeClause] Where C.[ModelEntityName]Uuid In {[SelectedUuids]} Select C.
Die TimeCockpit.Data.DataModel.ModelEntity ergibt sich aus der Liste oder dem Formular, in dem der Benutzer die Aktion aufgerufen hat. Ruft der Benutzer eine Aktion im Zeiterfassungskalender auf, ist die zugrunde liegende TimeCockpit.Data.DataModel.ModelEntity immer TimeSheet.
Die SelectedUuids ergeben sich aus dem Formular, den ausgewählten Elementen in der Liste oder den ausgewählten Elementen im Zeiterfassungskalender.
Die IncludeClause (mehr zu Include-Klauseln unter TCQL wird durch folgende Faktoren bestimmt:
Die Aktion kann eine Verarbeitungsdirektive # IncludeClause enthalten, die festlegt, welche Beziehungen einbezogen werden sollen (Details siehe Kapitel IncludeClause-Verarbeitungsdirektive).
Ist in der Aktion keine IncludeClause-Direktive angegeben, sucht time cockpit nach dem Standardformular der betroffenen TimeCockpit.Data.DataModel.ModelEntity. Hat das Formular eine
IncludeClausefestgelegt, wird dieseIncludeClauseverwendet. Das funktioniert nicht, wenn für das Standardformular einer TimeCockpit.Data.DataModel.ModelEntity ein Ausdruck angegeben ist. Da von einer Aktion mehrere Entitäten betroffen sein können, kann time cockpit kein eindeutiges Standardformular ermitteln, wenn ein Ausdruck verwendet wird.Gibt es in der Aktion keine IncludeClause-Direktive und lässt sich kein Standardformular ermitteln, verwendet time cockpit die Include-Klausel
.Include(*). Das bedeutet, dass alle Beziehungen rekursiv einbezogen werden. Haben die betroffenen Entitäten viele Beziehungen, kann die TCQL-Anweisung recht groß und damit langsam werden. Um die Performance der Aktion zu verbessern, fügen Sie eine IncludeClause-Direktive hinzu, die alle Beziehungen angibt, die zum Ausführen der Aktion tatsächlich nötig sind.
IncludeClause-Verarbeitungsdirektive
Eine Aktion kann optional eine Verarbeitungsdirektive # IncludeClause enthalten, die festlegt, welche Beziehungen einbezogen werden sollen. Der Code der Direktive muss in den ersten Zeilen des Codes der Aktion stehen, ohne Leerzeilen davor oder dazwischen. Die Direktive IncludeClause ist als Python-Kommentar umgesetzt. Sie hat folgende Syntax:
<includeClauseDirective> ::=
[ # IncludeClause
( NoInclude
| .Include(*)
| .Include(<relation_path>)[.Include(<relation_path>)...] ) ]
[ # SelectSettings.AutoIncludeRelations ( true | false ) ]
<relation_path> ::=
"<relation_name>[.<relation_name>...]"
Angenommen, die folgende Aktion wird für Zeitbuchungen verwendet. Die # IncludeClause legt fest, dass Tätigkeiten, Projekte, Kunden und Rechnungen einbezogen werden sollen.
# IncludeClause .Include('APP_Task.APP_Project.APP_Customer').Include('Invoice')
clr.AddReference("System")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Core")
...
Wenn Sie keine Beziehungen einbeziehen möchten, verwenden Sie folgende Verarbeitungsdirektive: # IncludeClause NoInclude. Beachten Sie, dass diese Direktive das Einbeziehen von Beziehungen nicht unterdrückt, die für berechnete Eigenschaften, Validierungsregeln usw. nötig sind.
Um festzulegen, dass nur die Beziehungen einbezogen werden, die Sie in der Direktive # IncludeClause .Include(...) ausdrücklich angeben, ohne automatisch die Beziehungen einzubeziehen, die für berechnete Eigenschaften, Validierungsregeln oder Berechtigungen nötig sind, geben Sie folgende Direktive an: # SelectSettings.AutoIncludeRelations false. Das ist sinnvoll, wenn Sie nur wenige Eigenschaften der ausgewählten Entitäten benötigen, zum Beispiel die Uuids der Entität.
Wenn Sie überhaupt keine Beziehungen einbeziehen möchten, müssen Sie # SelectSettings.AutoIncludeRelations false und # IncludeClause NoInclude kombinieren.
Hinweis
Verwenden Sie # SelectSettings.AutoIncludeRelations false niemals, wenn Sie die ausgewählten Entitäten in der Aktion speichern. Validierungsregeln und Berechtigungen funktionieren nicht wie erwartet, wenn die nötigen Beziehungen nicht geladen sind.