Search Results for

    Show / Hide Table of Contents

    Actions

    In time cockpit's model you can define Actions. Actions can be used for workflows, creating invoices, etc. In general you can use actions whenever you want to build a certain behavior that is not built in time cockpit out of the box.

    Structure of Actions

    Actions are represented as instances of the ModelAction class. You can add an action to the model using the ModelActions property.

    You can define conditions that have to be fulfilled in order to be able to execute an action. Every condition is represented by an instance of the Condition class or one of it's descendant classes. The most important condition is the ModelEntityTypeCondition. It can be used to specify that a certain action can only be used on instances of a certain entity type.

    An action can have an optional parameter. The parameter is represented by an instance of the ModelActionParameterDefinition class or one of it's descendant classes. A parameter can be used to ask a user for additional data that is necessary to perform the corresponding action.

    Last but not least every action must have a binding. The binding defines where the code for the action comes from. Currently time cockpit only supports actions written in Python. Therefore you have to use IronPythonBinding.

    Create and Update Actions

    The following example creates an action from a Python source file:

    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"
    

    The following example updates an action from a Python source file. It adds a filter entity to the action which is shown before the action is executed.

    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"
    

    Action Code

    The following example shows the code for an action. Note that the action code must only consist of Python method definitions. The action receives one parameter of type 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)
    

    Using the InputSet

    Before an action is executed, time cockpit reloads the corresponding data rows before it passes them to the action via the InputSet property. This is necessary to make sure that the entire rows including all dependent objects are in memory. A list could have been optimized to load only a subset of the columns of a model entity. If time cockpit would not reload the data rows before executing the action, the action would possibly get incomplete data.

    Warning

    Be aware that the input set does not guarantee a defined object order. Sorting may be required depending on the use-case.

    For reloading the entities, time cockpit generates a TCQL statement with the following structure: From C In [ModelEntityName][IncludeClause] Where C.[ModelEntityName]Uuid In {[SelectedUuids]} Select C.

    The ModelEntity is determined by the list or form in which the user has called the action. If the user calls an action in the time sheet calendar, the underlying ModelEntity is always TimeSheet.

    The SelectedUuids are determined by the form, the selected items in the list, or the selected items in the time sheet calendar.

    The IncludeClause (more about include clauses in TCQL is determined by the following factors:

    • The action can contain a processing directive # IncludeClause that specifies which relations should be included (details see chapter IncludeClause Processing Directive).

    • If no IncludeClause directive is specified in the action, time cockpit looks for the default form of the affected ModelEntity. If the form has specified an IncludeClause, this IncludeClause is used. This does not work if an expression is specified for the default form of a ModelEntity. As multiple entities may be affected by an action, time cockpit cannot determine one default form if an expression is used.

    • If there is no IncludeClause directive in the action and no default form can be determined, time cockpit uses the include clause .Include(*). This means that all relations are recursively included. If the affected entities have lots of relations, the TCQL statement can become quite large and therefore slow. To improve the performance of the action, add an IncludeClause directive that specifies all relations that are really necessary to run the action.

    IncludeClause Processing Directive

    An action can optionally contain a processing directive # IncludeClause that specifies which relations should be included. The directive code has to be in the first lines of the action's code with no empty lines before or in between. The IncludeClause directive is implemented as a Python comment. It has the following syntax:

    <includeClauseDirective> ::=
      [ # IncludeClause 
            ( NoInclude 
              | .Include(*)
              | .Include(<relation_path>)[.Include(<relation_path>)...] ) ]
      [ # SelectSettings.AutoIncludeRelations ( true | false ) ]
    
    <relation_path> ::=
      "<relation_name>[.<relation_name>...]"
    

    Let's assume the following action is used for time sheet entries. The # IncludeClause specifies that Tasks, Projects, Customers and Invoices should be included.

    # IncludeClause .Include('APP_Task.APP_Project.APP_Customer').Include('Invoice')
    
    clr.AddReference("System")
    clr.AddReference("PresentationFramework")
    clr.AddReference("System.Core")
    
    ...
    

    If you do not want to include relations, use the following include processing directive: # IncludeClause NoInclude. Note that this directive does not suppress including relations that are necessary for calculated properties, validation rules, etc.

    To specify that you want to include only the relations that you explicitly mention in the # IncludeClause .Include(...) directive without automatically including relations that are necessary for calculated properties, validation rules or permissions, specify the following directive: # SelectSettings.AutoIncludeRelations false. This is useful if you just need few properties of the selected entities like for example the Uuids of the entity.

    If you do not want to include any relations, you have to combine # SelectSettings.AutoIncludeRelations false and # IncludeClause NoInclude.

    Note

    Never use # SelectSettings.AutoIncludeRelations false if you will save the selected entities in the action. Validation rules and permissions will not work as expected if the necessary relations are not loaded.

    • Improve this Doc
    In This Article
    Back to top Copyright © 2020 software architects gmbh