2022 April Release

app.ducx Use Case LanguagePermanent link for this heading

The purpose of the app.ducx use case language is to define and implement use cases. With the app.ducx use case language you can define new use cases and provide method implementations for these use cases. You can use app.ducx expression language or virtual applications to implement use cases.

A use case model block consists of import declarations, transaction variable declarations, and use case model elements. The usecases keyword denotes a use case model block. It must be followed by the reference of your software component and curly braces.

Use case model blocks can only be contained in files with a .ducx-uc extension.

Syntax

usecases softwarecomponent
{
  // Import declarations
  import softwarecomponent;

  // Transaction variable declarations
  variables {
    datatype reference;
    ...
  }

  // Use case model elements
  ...
}

Declaring Transaction VariablesPermanent link for this heading

Syntax

variables {
  datatype reference = number;
  ...
}

In the usecases block, you can use the variables keyword to declare transaction variables provided by your software component.

Each transaction variable declaration consists of a valid Fabasoft app.ducx data type, the reference of the transaction variable to be declared and optionally a unique positive integer. If no numbers are specified, the system uses free numbers starting from 1.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  variables {
    boolean TV_PRINTINVOICE = 1;
    Invoice TV_INVOICE = 2;
  }
}

Defining a PrototypePermanent link for this heading

If there are multiple use cases with the same parameters, it is possible to define the parameters once as prototype with the keyword prototype. A prototype specifies the arguments of a use case, including the type, the name and the in/out settings.

Example

prototype AttrSetDatePrototype (
  AttributeDefinition attrdef,
  ref datetime value,
  in datetime oldvalue
);

There is also the possibility to specify expression parameters with the key words expression prototype.

Example

expression prototype boolean LogPrototype (
  string data,
  out string line
);

Defining a Method DefinitionPermanent link for this heading

To supply just an implementation without use case mapping, the keyword method can be used.

Example

method MDUpdateOwner {
  expression {
    cooobj.ObjectLock(true, true);
    cooobj.objowner = coouser;
  }
)

method MDEmpty {
  empty;
)

Note: if the implementation is for a specific use case, to supply the parameters of this use case at compile time the action has to be defined by using the keyword as:

Example

method MDUpdateObject {
  expression as ObjectPrepareCommit {
    /*
     * do anything

     */

    super();
  }
)

Defining Use CasesPermanent link for this heading

Th following types of actions can be invoked on an object:

  • An Action (COOSYSTEM@1.1:Action) is considered to be the declaration of a private method that is implemented on one or more object classes. Actions can be marked as public.
  • A Use Case (COOSYSTEM@1.1:UseCase) is considered to be the declaration of a public method. It can provide different implementations on one or more object classes. Use cases can be marked as private.

For more information on public, private and obsolete see chapter “Public, Private, Secured, Obsolete”.

This section describes the definition of use cases using the app.ducx use case language of Fabasoft app.ducx.

Defining a New Use CasePermanent link for this heading

Syntax

usecase reference(parameter, ...) {
  accexec = accesstype;
  // Providing the implementation for one object class
  variant objectclass {
    ...
  }
  // Providing the same implementation for more object classes
  variant objectclass1, objectclass2 {
    ...
  }
}

The usecase keyword is used to declare a use case. It must be followed by the reference and by parentheses, holding the list of parameters or the prototype. Use cases define the public interface of the software component.

Note:

  • An action is declared the same way, using the optional action keyword. Actions are meant to be private.
  • The general data type aggregate cannot be used as argument type, please use interface instead,

Defining the List of ParametersPermanent link for this heading

Parameters must be separated by commas. For each parameter, the data type must be specified. For objects, use the object keyword or the reference of an object class when denoting the data type. For specifying an arbitrary data type, use the any keyword. Square brackets can be used to denote lists.

By default, parameters are treated as input parameters. Output parameters must be prefixed with the keyword out. For input/output parameters, the prefix ref must be used. The keywords retval out (out may be omitted) and retval ref denote parameters that are used as default return values. The keyword optional can be used to mark optional parameters. unique specifies unique lists in parameters.

... can be used to mark an unspecified number of optional parameters at the end of the parameter list. The interface coometh.GetParameter() provides access to these arguments.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase CalcOrderPosValue(integer quantity, currency unitprice,
    out currency total, optional float rebate) {
    ...
  }

  usecase CalcOrderTotal(OrderPosition[] positions, boolean includetax,
    out currency total, out currency tax) {
    ...
  }

  usecase ValidateOrderPositions(ref unique OrderPosition[] positions) {
    ...
  }

  usecase CreateCollectiveInvoice(Order[] orders, retval Invoice invoice) {
    ...
  }

  usecase MyPrint(string format, ...) {
    ...
  }
}

Note: Even if you mark a parameter with retval it is not allowed to use return with a value in a method implementation. The only way to provide return values of a method is by assigning values to parameter marked as ref, out or retval.

Using Prototypes Instead of a Parameter ListsPermanent link for this heading

Prototypes are component objects containing predefined sets of parameters. Certain use cases – such as property triggers described in chapter “Assigning Triggers to a Property” – require predefined prototypes in order to function correctly.

For assigning a prototype to a use case instead of specifying a list of parameters you can use the parameters as keywords followed by the reference of the prototype.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  // Skeleton of a get value trigger action
  action GetOrderTotal(parameters as AttrGetPrototype) {
    ...
  }

  // Skeleton of a set value trigger action
  action SetOrderPositions(parameters as AttrSetPrototype) {
    ...
  }
}

Protecting a Use Case With an Access TypePermanent link for this heading

The accexec keyword is used to assign an access type to a use case.

If protected by an access type, a use case can only be invoked if the user trying to invoke it is granted the access type by the object’s ACL. However, it is not mandatory to protect a use case with an access type.

Enable CachingPermanent link for this heading

The cacheable keyword is used to enable caching for the action call in a current transaction.

The action call results are reused

  • if the transaction is equal.
  • if the object and the action are equal.
  • if the in and inout parameters are equal.

A transaction commit or abort clears the cache.

Providing the Implementation for a Use CasePermanent link for this heading

In order to be able to invoke a use case, you have to provide an implementation for one or more object classes. You may also provide different implementations of the same use case for different object classes just as in an object-oriented programming language, Fabasoft app.ducx allows an object class to provide a more specific implementation of a use case that is already implemented by one of its base classes. In this case, the implementation in the subclass overrides the implementation in the base class.

The kernel returns an error message if a use case is invoked on an object where no implementation is found in the object class of the object or in one of its base classes.

The variant keyword is used to denote the object classes for which an implementation of a use case is provided. There are various types of implementations:

  • The application keyword specifies a virtual application that is called when the use case is invoked. A virtual application is required when your use case implementation must display user interface elements. Please refer to chapter “Defining a Virtual Application” for further information on implementing virtual applications.
  • The expression keyword defines a use case implementation in app. ducx expression language.
  • The empty keyword defines an empty implementation that does nothing.
  • The impl keyword means implementing the use case with an application or with a method definition, See the chapter “Defining a Virtual Application” for more information.
  • If you want to provide the same implementation for multiple object classes, you can enumerate these object classes in a comma separated list after the variant keyword.

In a variant block, you may also define optional use case hints to describe the behavior of the use case implementation. To define use case hints, use the hints keyword, followed by curly braces and a list of use case hints as described by the following table.

Hint

Description

MH_CHANGESOBJ

The use case implementation changes the current object.

MH_NEEDSSERVER

The use case implementation requires a connection to the server.

MH_NEEDSARCHIVE

The use case implementation requires an archive implementation.

MH_NEEDSLOCALGUI

The use case implementation requires interaction with the graphical user interface.

MH_CHANGESVIEW

The use case implementation changes the current view.

MH_CREATESOBJ

The use case implementation creates a new object in the currently selected object pointer or object list.

MH_NEEDSEXPLORER

The use case implementation requires explore mode.

MH_NEEDSTREE

The use case implementation requires the tree view

MH_NEEDSPLUGIN

The use case implementation requires the client plugin

MH_NEEDSSINGLESEL

The use case implementation requires a single selection

MH_NEEDSMULTISEL

The use case implementation requires a multiple selection

MH_NEEDSDESKTOPORTABLET

The use case implementation requires at least a Desktop or a Tablet.

MH_NEEDSCURRENTVERSION

The use case implementation requires the object in the current version, not a historic version.

Implementing a Use Case in app.ducx Expression LanguagePermanent link for this heading

The app.ducx expression language can be used for implementing a use case. When implementing a use case as expression, the current object – that is the object on which the use case has been invoked – can be accessed using the cooobj variable. Additionally, the coort interface object can be used to access the runtime, and the cootx variable contains an interface to the current transaction. Parameter names can be used to access the parameters declared in the use case signature.

Note: The app.ducx compiler verifies the syntax of expressions, but it cannot completely verify semantic correctness.

In the following example, the use case CreateInvoice is implemented in object class APPDUCXSAMPLE@200.200:Order. The expression creates a new instance of object class APPDUCXSAMPLE@200.200:Invoice, initializes some of its properties, passes back the invoice object in parameter invoice, and references the new invoice in the APPDUCXSAMPLE@200.200:invoice property of the order.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase CreateInvoice(retval Invoice invoice) {
    variant Order {
      expression {
        Invoice invoice = #Invoice.ObjectCreate();
        invoice.invoiceorder = cooobj;
        invoice.invoicedate = coonow;
        cooobj.ObjectLock(true, true);
        cooobj.objchildren += invoice;

      }
      hints = {
        MH_NEEDSSERVER,
        MH_CHANGESOBJ
      }
    }
  }
}

If a use case overrides one of its superclass's use cases, you can invoke the overridden use case through the use of the keyword super. Calling super is only allowed when coometh (holds the so-called method context) is available. in or ref parameters that are changed before the call of super are passed in the changed state. out or ref parameters are written to the local scope after the call of super.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase CallSuper(integer inx, retval string name) {
    variant Object {
      expression {
        name = inx;
      }
    }
    variant CompoundObject {
      expression {
        inx++;
        super();
        %%TRACE("name = ", name);
      }
    }
  }
}

Defining a New Menu Use CasePermanent link for this heading

Syntax

menu usecase reference on selected {
  variant objectclass {
    ...
  }
}

menu usecase reference on selected or container {
  variant objectclass {
    ...
  }
}

menu usecase reference on container {
  variant objectclass {
    ..
  }
}

menu usecase reference dynamic {
  get = CalculateMenu;
  variant objectclass {
    ...
  }
}

A menu use case is a special type of use case that is usually invoked by a user selecting a menu item in the user interface.

There are a few optional options available, specified by using keywords after the reference:

  • on selected
    The menu use case is invoked on each object selected by the user. This is the default behavior.
  • on selected or container
    The menu use case is invoked on each object selected by the user. However, if no objects have been selected, the menu use case is invoked on the container object instead. For example, if you are invoking a menu use case from within a folder but no objects have been selected in the folder, it is invoked on the folder itself.
  • direct or on container
    The menu use case is invoked on the container object only. It is NOT invoked on any objects selected by the user. However, it is possible to get the currently selected objects by using COODESK@1.1:GetSelected from within the implementation of the menu use case. If the use case is implemented as virtual application, the selected objects are also available in the sys_selobjects parameter.
  • dynamic
    The displayed menu is calculated by an action specified by the alias get, which is a shortcut for menugetaction. This action has to use the prototype COODESK@1.1:MenuGetActionPrototype. The menu use case is invoked on the container object only. The parameter sys_dynkey contains the index of the selected menu item.

To enable this functionality, a number of elements are required. These are generated automatically by the app.ducx compiler:

  • the menu object that can be added to a menu or a context menu,
  • the outer use case that is invoked on the container object (when using the on selected or on selected or container clause),
  • the implementation of the outer use case using method definition COODESK@1.1:CallSelectedObjectsMethod in object class COOSYSTEM@1.1:Object (when using the on selected or on selected or container clause),
  • the application mapping of the outer use case to FSCVENV@1.1001:DoSelected (when using the on selected or on selected or container clause), and
  • the inner use case that is invoked on each of the selected objects.

The following example shows the definition of a menu use case for setting the state of an instance of object class APPDUCXSAMPLE@200.200:Order to OS_SHIPPED. This menu use case is invoked on each of the order objects selected by the user. For this menu use case, the Fabasoft app.ducx compiler also generates a menu object with the reference APPDUCXSAMPLE@200.200:MenuMarkOrderAsShipped. In the example, this menu object is added to context menu APPDUCXSAMPLE@200.200:MenuRootOrderContext to allow users to invoke the use case from the context menu of an order to change the state of the order to OS_SHIPPED.

Note: Objects must be locked before they may be changed. To lock an object, COOSYSTEM@1.1:ObjectLock must be invoked on the object.

Example

app.ducx Use Case Language

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  menu usecase MarkOrderAsShipped on selected {
    // Use defined symbol in ressources file
    symbol = SymbolMarkOrderAsShipped;
    variant Order {
      expression {
        cooobj.ObjectLock(true, true);
        cooobj.orderstate = OrderState(OS_SHIPPED);
      }
    }
  }
}

app.ducx User Interface Language

userinterface APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import COODESK@1.1;

  // Assign the use case to the context menu of APPDUCXSAMPLE@200.200:Order
  menus for Order {
    MenuContextExpansion {
      MenuMarkOrderAsShipped;
    }
  }
}

Note: Menu use cases can also be implemented as a virtual application. If implemented as virtual application, a predefined prototype is assigned to the virtual application.

For menu use cases employing the on selected or on selected or container clause the FSCVENV@1.1001:MenuPrototype prototype is assigned to the virtual application, and the parameters described in the following table are passed to it.

Variable

Description

venv_object

The variable venv_object stores the object the use case was invoked on.

venv_parent

The variable venv_parent stores the container from which the use case was executed.

venv_index

The variable venv_index stores the index of the object in the property from which the use case was executed.

venv_view

The variable venv_view stores the property from which the use case was executed.

venv_action

The variable venv_action stores the use case that is executed.

For menu use cases employing the direct or on container clause the FSCVAPP@1.1001:MenuPrototype prototype is assigned to the virtual application, and the parameters described in the following table are passed to it.

Variable

Description

sys_object

The variable sys_object stores the object the use case was invoked on.

sys_action

The variable sys_action stores the use case that is executed.

sys_view

The variable sys_view stores the property from which the use case was executed.

sys_selobjects

The variable sys_selobjects stores the objects selected in the property stored in variable sys_view.

sys_selindices

The variable sys_selindices stores the index of each selected object in the property stored in variable sys_view.

sys_dynkey

The variable sys_dynkey stores the key of the dynamic menu and is only available when the use case was executed from a dynamic menu.

Defining a Virtual ApplicationPermanent link for this heading

Use cases and menu use cases can be implemented as virtual applications.

If at any point your use case needs to present a user interface – such as a form or a dialog box – you are required to implement it as a virtual application. This is also the case when you invoke another use case requiring user interaction from your use case.

A virtual application is composed of expressions and dialogs. Dialogs allow you to display a form or form page, and have a set of branches.

When a use case is implemented as virtual application, the Fabasoft app.ducx compiler generates an instance of object class Application (FSCVAPP@1.1001:Application) for the virtual application, usually with the same reference as specified for the use case. Moreover, an instance of object class Application View (FSCVAPP@1.1001:ApplicationView) is generated for each dialog defined.

The application keyword denotes the definition of a virtual application. An application block can contain declarations of global variables, an expression block, multiple inline definitions of dialogs and generic assignments. Each dialog must be defined within its own dialog block embedded in the application block. In order to be displayed, a dialog must be called explicitly from within the expression block using the detachment operator ->.

Global variables must be declared before the expression block by denoting data type and the variable name followed by a semicolon. Once declared, global variables are available within the scope of the entire virtual application including all its dialogs. For example, if you need to increase a counter as your virtual application is processed you should to declare a global variable for the counter.

In addition to global variables, you can also reference global fields that are available throughout your virtual application. Global field must be declared before the expression block. The keyword field must be used for declaring a global field. It must be followed by its reference and a semicolon.

Note: You may only reference fields that have been defined in a fields block using the Fabasoft app.ducx object model language.

Syntax

usecase reference(parameter, ...) {
  variant objectclass {
    application {
      // Virtual applications can consist of global variable declarations,
      // global fields, expression blocks and dialogs that can be defined

      // inline

      datatype variable;
      field reference;
      ...
      expression {
        ...
      }
      overlaysize = overlaysizevalue;

      dialog reference dialogmodifiers {
        // Initialization expression that is executed before the dialog
        // is displayed

        expression {
          ...
        }
        form = form;
        target = target;
        value = valuetype;
        autoload = booleanvalue;
        autostore = booleanvalue;
        autolock = booleanvalue;
        catcherrors = booleanvalue;
        overlaysize = overlaysizevalue;
        branch identifier branchmodifiers {
          caption = captionstring;
          description = descriptionstring;
          symbol = symbol;
          visible = expression {
            ...
          }
          expression {
            ...
          }
          applytofield<view, identopt> = {
           { form, field }
          }
        }
        ...
        behaviors {
          behavior {
            condition = expression {
              ...
            }
            applytofield<view, identopt> = {
              { form, field },
              ...
            }
          }
          ...
        }
      }
    }
  }
}

Applications can only be called in the expressions of other applications or branches, either directly or indirectly by using the detach operator.

Implementing a Use Case With a Virtual ApplicationPermanent link for this heading

A virtual application is implemented as a list of statements written in app.ducx expression language.

As usual, the cooobj variable holds the current object on which the use case is invoked. cootx and coort are also available for accessing the current transaction and the runtime.

Parameters of a Virtual ApplicationPermanent link for this heading

The parameters expected by the virtual application can be specified in two ways:

  • The application uses the parameter list of the use case.
  • The application specifies a parameter list explicitly.

They are instantly available in the expression code like variables.

In the example, a use case for adding a new entry to the list of order positions of an order is implemented as a virtual application. The parameters product and quantity – that must be supplied when the use case is invoked – are instantly available as variables in the expression block. Moreover, the detachment operator, which will be discussed later in this chapter, is used to invoke an existing use case for editing the properties of the order.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase AddPositionToOrder(Product product, integer quantity) {
    variant Order {
      application {
        expression {
          cooobj.ObjectLock(true, true);
          OrderPosition position = OrderPosition();
          position.product = product;
          position.quantity = quantity;
          cooobj.orderpositions += position;
          cooobj->COOATTREDIT@1.1:EditObjectAttributes();
        }
      }
    }
  }
}

Invoking Use Cases, Virtual Applications, and DialogsPermanent link for this heading

Expressions that are hosted within a virtual application or within a dialog can make use of the detachment operator -> in order to invoke another use case, a virtual application, or a dialog.

Note: Instead of statically specifying the use case, virtual application or dialog, you can use square brackets for dynamic evaluation of the use case, virtual application or dialog to be invoked.

Example

// static invocation of a use case
cooobj->COODESK@1.1:OpenObject();

// dynamic invocation of a use case
UseCase uc = #COODESK@1.1:OpenObject;
cooobj->[uc]();

Invoking a DialogPermanent link for this heading

For invoking a dialog, the detachment operator -> must be followed by the reference of the dialog to be displayed. A dialog does not have any parameters. Supplying parameters when invoking a dialog leads to a runtime error.

Using the detachment operator, you can invoke any of the dialogs defined in the application block of your virtual application. You can also invoke dialogs that have been defined in other applications for reasons of reusability. However, reusing dialogs is strongly discouraged since unlike use cases and virtual applications they are not self-contained units with a defined interface.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  menu usecase CreateOrderWizard on selected {
    variant ContactPerson {
      application {
        integer currentstep; // Global variable for storing step index
        Order neworder;      // Global variable for storing the new order
        expression {
          currentstep = 1;
          ->WizardStep1;
        }
        dialog WizardStep1 {
          ...
        }
        dialog WizardStep2 {
          ...
        }
        dialog WizardStep3 {
          ...
        }
      }
    }
  }
}

Invoking a Virtual ApplicationPermanent link for this heading

A virtual application can be invoked with the detachment operator -> followed by the reference of the virtual application and the list of parameters enclosed in parentheses. Parameters may not be omitted. Thus, all parameters defined in the parameter list of the virtual application to be invoked must be specified. However, you can use null if you do not want to provide a value for an optional parameter.

Note: You should favor invoking a use case instead of a virtual application, whenever possible (see chapter "Invoking Another Use Case”).

The following example demonstrates invoking the virtual application FSCVENV@1.1001:EditObjectAttributeApp.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  menu usecase EditInvoice on selected {
    variant Order {
      application {
        expression {
          Invoice invoice = cooobj.orderinvoice;
          if (invoice != null) {
            ->FSCVENV@1.1001:EditObjectAttributeApp(invoice, cooobj, null,
              #orderinvoice, null, false, null, false);
          }
          else {
            throw #NoInvoiceFound;
          }
        }
      }
    }
  }
}

Invoking Another Use CasePermanent link for this heading

When invoking another use case from a virtual application, you have to specify an object on which the use case is to be called. The object must be followed by the detachment operator, the reference of the use case to be called, and the list of parameters enclosed in parentheses.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase OpenInvoice() {
    variant Order {
      application {
        expression {
          Invoice invoice = cooobj.orderinvoice;
          if (invoice != null) {
            invoice->COODESK@1.1:OpenObject();
          }
          else {
            throw #NoInvoiceFound;
          }
        }
      }
    }
  }
}

Special Variables in Virtual ApplicationsPermanent link for this heading

When a virtual application or a dialog is invoked, the Fabasoft vApp engine – depending on the context – makes available a set of special variables in the local scope this. As already stated, some of these variables will only be available in certain situations, e.g. when the user executes a branch. These special variables are described in the following table.

Variable

Description

sys_object

The variable sys_object stores the current object or the container object when a menu is invoked.

sys_action

The variable sys_action stores the use case or action that is executed.

sys_view

The variable sys_view stores the currently selected property when a menu or a branch is executed.

sys_selobjects

The variable sys_selobjects stores the list of objects selected in the list associated with the executed branch.

sys_selindices

The variable sys_selindices stores the list of indices selected in the list associated with the executed branch.

sys_branchvalue

The variable sys_branchvalue stores the current value of the data field associated with the executed branch.

sys_branchattr

The variable sys_branchattr stores the full path to the property of the data field associated with the executed branch.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;

  menu usecase ShipSelectedOrders on selected {
    variant ContactPerson {
      application {
        expression {
          ->SelectOrder;
        }
        dialog SelectOrder {
          form = SelectOrders;
          target = cooobj;
          cancelbranch;
          nextbranch {
            expression {
              if (sys_selobjects > 0) {
                sys_selobjects-> ShipOrder();
              }
              else {
                throw #NoObjectsSelected;
              }
            }
          }
        }
      }
    }
  }
}

Virtual Application ContextPermanent link for this heading

The action FSCVAPP@1.1001:GetContext allows you to access the virtual application context interface exposing the GetServerVariable and GetFormVariable methods.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase GetvAppInfos() {
    variant Object {
      application {
        expression {
          vappctx = coouser.FSCVAPP@1.1001:GetContext();
          if (vappctx != null) {
            cookies = vappctx.GetServerVariable("HTTP_COOKIE");
            querystring = vappctx.GetServerVariable("QUERY_STRING");
            useragent = vappctx.GetServerVariable("HTTP_USER_AGENT");
            remoteuser = vappctx.GetServerVariable("REMOTE_USER");
            remoteaddress = vappctx.GetServerVariable("REMOTE_ADDR");
            formvar_firstname = vappctx.GetFormVariable("firstname");
            formvar_surname = vappctx.GetFormVariable("surname");
            %%TRACE("Local variables", this);
          }
        }
      }
    }
  }
}

The following example demonstrates how to post an HTML form to a virtual application (assuming a default installation of the web services on a host named “folioweb”).

In the ax parameter, the address of the virtual application must be provided (for the example, assume that COO.200.200.1.1000 is the address of the GetvAppInfos virtual application from the previous example). The return URL that is invoked after submitting the HTML form must be provided in the ru parameter.

To close a window or overlay the cm parameter can be used: cm=1 (close window), cm=2 (close overlay).

Note: For the HTML field values to become available in the virtual application context, the URL parameter xhr=s must be added to the virtual application URL.

Example

<html>
  <body>
    <form action="http://folioweb/fsc/fscasp/content/bin/fscvext.dll?
      ax=COO.200.200.1.1000&ru=http%3A%2F%2Fwww.fabasoft.com&xhr=s"
method="post">
      <label for="firstname">First Name: </label>
      <input type="text" name="firstname"><br>
      <label for="surname">Surname: </label>
      <input type="text" name="surname"><br>
      <input type="submit" value="Send"> <input type="reset">
    </form>
  </body>
</html>

Direct Assignments to a Virtual ApplicationPermanent link for this heading

The overlay size for an application can be defined by using a generic assignment of the enumeration type FSCVAPP@1.1001:OverlaySize to the property FSCVAPP@1.1001:overlaysize. This setting is used for all dialogs in the virtual application except a dialog has an own definition.

Extending a Virtual ApplicationPermanent link for this heading

A virtual application can be extended with dialogs in context of a use case and variant. Use the using keyword followed by the reference of a virtual application and curly braces. By default, this reference is formed by concatenating the variant name and the use case name.

Syntax

using <reference of application> {
  dialog reference {
    ...
  }
}

In the following example the virtual application for the menu use case “EditInvoice” that is defined for variant “Order” gets extended by the “EditOrder” dialog.

Example

using OrderEditInvoice {
  dialog EditOrder {
    form = expression {
      if (coort.GetCurrentUserRoleGroup() == #SysAdm) {
        return #FormEditOrderAdmin;
      }
      else {
        return #FormEditOrderUser;
      }
    }
  }
}

Defining a DialogPermanent link for this heading

A dialog is defined within a dialog block nested within the application block of your use case. You can define multiple dialogs within an application block.

Note: The dialog blocks must not precede the expression block within an application block. The expression block must be the first block within an application block.

For a comprehensive example illustrating the definition of a dialog, please refer to the end of this section.

Assigning a FormPermanent link for this heading

You can use the form keyword to assign a form or form page to a dialog, which is shown when the dialog is invoked.

Note: Referencing an instance of object class View (FSCVIEW@1.1001:View) or Tabbed View (FSCVIEW@1.1001:TabbedView) is not supported any more.

The form to be assigned to a dialog can also be dynamically calculated using app.ducx expression language. When an app.ducx expression is used, it must return the form, form page or view to be displayed on the dialog.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  menu usecase EditOrder on selected {
    variant Order {
      application {
        expression {
          ->EditOrder
        }
        dialog EditOrder {
         form = expression {
          
if (coort.GetCurrentUserRoleGroup() == #SysAdm) {
            
return #FormEditOrderAdmin;
            }
           else {
            
return #FormEditOrderUser;
            }
          }
          overlaysize = OVERLAYSIZE_LARGE;
        }
      }
    }
  }
}

Choosing the View ModePermanent link for this heading

A dialog has two view modes: edit mode and read-only mode. When in edit mode, users are allowed to enter values into the fields displayed on the form. Otherwise, fields are not editable in the user interface.

By default, each dialog defined is displayed in edit mode. If you want to define a read-only dialog, the readonly dialog modifier suffix must be denoted following the dialog’s reference.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  menu usecase ViewInvoice on selected {
    variant Invoice {
      application {
        expression {
          ->ViewInvoice
        }
        dialog ViewInvoice readonly {
          ...
        }
      }
    }
  }
}

Enabling Query ModePermanent link for this heading

A dialog has two view types: value mode and query mode. Query mode is a special mode for creating query forms. To enable query mode, the value keyword must be set to VALUETYPE_QUERY.

When in query mode, a dialog must be initialized with a query following the app.ducx Query Language syntax. This query must be passed to the dialog in the sys_query system variable. The user can then use the dialog to enter additional query conditions, and the resulting query is returned in the sys_query system variable.

Assigning a Target ObjectPermanent link for this heading

For each dialog, you can assign a target object on which it is operating. The vApp engine provides a mechanism for automatically loading and storing property values from and to the target object. The target keyword is used to assign a target object to a dialog. Usually, the name of a variable holding the desired target object is specified as the target. Additionally, the target can be defined as app.ducx expression.

If no explicit target is defined, the target is the value of cooobj of the calling application.

The targetwindow keyword can be used to define the target window of the virtual application (TARGETWINDOW_SAME, TARGETWINDOW_NEW, TARGETWINDOW_OVERLAY). The default value is TARGETWINDOW_OVERLAY. If a virtual application calls another virtual application and so on, the value from the last called virtual application that has defined a target window is used.

The transaction keyword defines whether the virtual application is executed in a new transaction context (TRANSACTION_SAME, TRANSACTION_NEW, TRANSACTION_NEWEXPLORE). The default value is TRANSACTION_SAME (in some special cases the default value may differ).

Automatic Loading and Storing of Field ValuesPermanent link for this heading

By default, property values are read from the database and displayed in the corresponding fields on the form when a dialog is invoked.

When the user leaves the dialog by executing a branch, changes by the user are automatically saved – provided that the branch expression discussed later does not throw an exception.

This feature, however, requires that a target object is assigned to a dialog using the target keyword.

Fabasoft app.ducx allows you to suppress automatic loading of field values by setting autoload to false for a dialog. Conversely, the autostore keyword can be used to disable storing field values when leaving a dialog.

For instance, autostore should be set to false for a dialog that is set to read-only view mode as it would not make sense to write back values to the target object that cannot be changed in the user interface anyway.

Defining Overlay SizePermanent link for this heading

The overlay size for a dialog can be defined by using a generic assignment of the enumeration type FSCVAPP@1.1001:OverlaySize to the property FSCVAPP@1.1001:overlaysize.

Defining a Confirm DialogPermanent link for this heading

A confirm dialog is used to confirm an action executed by the user, to allow the user to choose from different options (e.g. whether he wants to delete an object or whether a version should be created) or to show error messages or warnings. These dialogs are shown in a special way (as small overlays with the branches at the bottom). The pagemode property is used to define a dialog as confirm dialog.

Example

usecases APPDUCXSAMPLE@200.200
{
  ...
  dialog ConfirmOrder {
    target = orderobj;
    pagemode = PAGEMODE_CONFIRM;
    form = FormConfirmOrder;
    ...
  }
}

Defining the Title, a Heading, and a Description of a DialogPermanent link for this heading

In general, you may display a title, a heading and a short description on a dialog. The title should name the current use case so that the user is able to see the purpose of the current action. A good approach is to use the name of the menu item by which the use case was triggered. The title is also part of the HTML title tag.

In the heading you should prompt the user to execute something (e.g. “Choose the versions you want to delete”), ask a question (e.g. “Which version of “My Doc” do you want to view?”), or describe the action in more detail. If possible, the heading should fit into one line but it must differ from the title. You may include the name of the object into the heading. On multi-page dialogs tabs will be displayed instead of the heading.

The description provides additional hints and instructions to the user if these are necessary for filling out the data or for finishing the use case.

On a confirm dialog you only have a title and a description. Confirm dialogs do not consist of multiple pages.

If the form of a dialog is assigned by a dynamic binding the dialog title may be determined by the property formcaption of the form. Otherwise the multilingual name (mlname) of the dialog is used.

The heading of a dialog is determined by the property formpagelabel of the form page. If it is empty no heading is shown.

The description of a dialog is determined by the property formpagedescription of the form page. If formpagedescription is not set, the property description of the dialog is used.

You may use placeholder with inline expression code to generate the strings for these properties dynamically. A placeholder is delimited by “<~” and “~>”. Everything between the delimiters will be interpreted as expression. The expression must return a string.

Example

usecases APPDUCXSAMPLE@200.200
{
  ...
  dialog DialogApproveInvoice {
    target = orderobj;
    description = {}
    form = FormApproveInvoice;
    ...
  }
}

userinterface
APPDUCXSAMPLE@200.200
{
  ...
  form FormApproveInvoice {
    formpage PageInvoiceData {
      ...
  }
}

// mlnames.lang file:
DialogApproveInvoice.mlname = "Approve Invoice"
PageInvoiceData.formpagelabel = "Do you want to accept the invoice
   \"<~ cooobj.objname ~>\"?"
DialogApproveInvoice.description = "Check the data below and choose button
   <~ #APPDUCXSAMPLE@200.200:StrAcceptButton.Print() ~> if everything is correct."

Defining Dialog BehaviorsPermanent link for this heading

Behaviors allow you to customize the look and feel of a dialog or the fields on the form page displayed by the dialog.

Behavior definitions must be contained in a behaviors block nested within a dialog block. The behaviors keyword must be followed by curly braces.

Within the behaviors block, the keyword representing the corresponding behavior must be denoted, followed by curly braces. The same definition block can be used for multiple behaviors. In this case, the behavior keywords must be separated by commas. The following table shows a list of supported behaviors along with the respective keywords that must be used.

Additional to the listed keywords all behaviors defined in the enumeration type Hint (FSCVAPP@1.1001:HintType) can be used. Instead of the keyword the Reference Name (e.g. VIEWHINT_FLAT) denotes the behavior.

Behavior

Description

genericform

The genericform behavior is used to specify a pattern to match an action that selects a form; the pattern may start with "explore:" to match an explore form. When applied to a field, genericform determines whether the generic built-in functionality for displaying the field – such as tooltips and context menus – should be enabled.

symbol

The symbol behavior is used to specify whether a row symbol should be displayed for the entries of lists.

selection

The selection behavior is used to specify whether the selection of one or more rows should be enabled for lists.

required

The required behavior is used to specify whether a field requires the user to enter a value.

editcondition

The editcondition behavior is used to specify a condition that must evaluate to true in order to make a field editable. If the condition returns false, the selected field is displayed in read-only mode.

viewcondition

The viewcondition behavior is used to specify a condition that must evaluate to true in order to make a field visible. If the condition returns false, the selected field is not displayed on the user interface.

reference

The reference behavior is used to specify that component object references should be displayed instead of the object names in the object name column of object lists.

exploremode

The exploremode behavior is used to specify that object lists should be displayed on separate pages on the user interface.

simple

The simple behavior is used to enable the simple view.

showmultifunctionbar

The showmultifunctionbar behavior is used to enable or disable the multifunctional bar. By default, the multifunctional bar is enabled.

menus

The menus behavior is used to enable or disable menu bars for a dialog. By default, menus are enabled.

contextmenus

The contextmenus behavior is used to enable or disable context menus for a dialog. By default, context menus are enabled.

sort

The sort behavior is used to enable or disable sorting for object lists and compound lists. By default, sorting is enabled.

create

The create behavior is used to enable or disable creating new instances for object pointer properties and object lists.

search

The search behavior is used to enable or disable search functionality for object pointer properties and object lists.

Each behavior can be applied to one or more fields of a form page or the dialog itself. An applytofield initialization block must be used to apply a behavior block to a specific field or to a list of fields. applytofield is a compound property, but in many cases only the identopt property of the compound property is used. Therefore, the value for identopt can be assigned directly to applytofield (e.g. applytofield = "objname";). Please be aware that identopt is the short reference of the field.

Furthermore, the condition keyword can be used to define a condition in app.ducx expression language. When a condition block is defined within a behavior block, the behavior is only applied if the expression returns true.

Defining BranchesPermanent link for this heading

When described in an oversimplified manner, a branch just defines a button and a handler that is invoked when the button is clicked. In Fabasoft terminology, “clicking the button” is referred to as “executing the branch”.

The branch keyword must be used for defining a branch, followed by a unique branch identifier (which is also referred to as the programming name) and curly braces. Multiple branch blocks can be nested within a dialog.

Assigning Branch ModifiersPermanent link for this heading

The branch identifier can be followed by optional branch modifiers. The following table shows a list of supported branch modifiers. If multiple branch modifiers are assigned to a branch, they must be separated by whitespaces.

Modifier

Description

leave

If the leave branch modifier is provided, the vApp engine continues processing the virtual application with the statement following the statement invoking the current dialog. If the leave branch modifier is omitted, the user is taken back to the current dialog after the branch has been executed successfully.

default

The default branch modifier should be applied to the default branch. There can only be one default branch for each dialog.

hyperlink

The hyperlink branch modifier allows you to attach a branch to a value displayed by a field on the form. The field’s value will then behave like a hyperlink, so a user can click on the value to activate the branch. A hyperlink branch does not have a caption.

skipnull

The skipnull branch modifier allows a user to invoke a branch even though no value was provided for one or more required fields on the form. When the skipnull branch modifier is omitted, a user is not able to invoke a branch before values are provided for all required fields.

Assigning Symbol, Caption, and Description TextPermanent link for this heading

A symbol can be assigned to a branch using the symbol keyword. Please refer to chapter “Defining Symbols” for detailed information on how to define a new symbol.

The caption text displayed on a branch is usually loaded from a String (COOSYSTEM@1.1:String) object, which can be assigned to a branch using the caption keyword. Likewise, the description keyword is used to assign a description string to a branch, which is displayed when the user hovers the mouse over the branch. Please consult chapter “Defining Strings” for information on how to define new strings.

The caption string and the description string can contain embedded expressions for dynamic text.

Note: The compiler cannot validate expressions inside strings. Best practice is to embed calculated variables only.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCVAPP@1.1001;
  import FSCVENV@1.1001;

  menu usecase ShowDynamicBranch {
    variant Object {
      application {
        string nr;
        string countcaption;
        expression {
          nr = 0;
          ->DynamicDialog;
        }
        dialog DynamicDialog {
          expression {
            countcaption = "$$" + nr + "$$";
          }
          branch count {
            caption = StrCountCaption;
            expression { nr++ }
          }
          cancelbranch;
        }
      }
    }
  }
}

resources APPDUCXSAMPLE@200.200
{
  string StrCountCaption; // English: <%countcaption%>
}

Ignoring Field ValuesPermanent link for this heading

By default, the vApp engine does not execute a branch handler expression if not all required fields on the current form actually contain a value. Instead, an error message is displayed requesting the user to supply values for all required fields.

In order to force the vApp engine not to check whether required fields contain a value, you can use the skipnull branch modifier.

You will typically use skipnull in Cancel branches for aborting the execution of a virtual application.

Dynamically Hiding a BranchPermanent link for this heading

The visible keyword allows you to define an app.ducx expression for evaluating whether a branch should be displayed. If the expression returns true, the branch is displayed, otherwise it is hidden.

Attaching a Branch to a FieldPermanent link for this heading

A branch can be attached to a field displayed on the form. An applytofield block must be used for attaching a branch to a field. applytofield is a compound property, but in many cases only the identopt property of the compound property is used. Therefore, the value for identopt can be assigned directly to applytofield (e.g. applytofield = "objname";).

If a branch is attached to an object list or a compound list, it is displayed above the field. If it is attached to property containing a scalar value, it is displayed on the left-hand side next to the field.

Branches can also be attached to properties that are part of a compound property displayed on the form.

The hyperlink branch modifier allows you to attach the branch to the value displayed by a field. If it is omitted, the branch will be displayed next to the field value.

Defining the Branch Handler ExpressionPermanent link for this heading

You can use app.ducx expression language to implement a branch handler. The branch handler expression is defined in an expression block nested within the branch block.

You can use the detachment operator for invoking another dialog, or for executing another virtual application or a use case. For further information on how to use the detachment operator, please refer to chapter “Invoking Use Cases, Virtual Applications, and Dialogs”.

Predefined BranchesPermanent link for this heading

The majority of all dialogs contain some common branches: a “Next” branch to proceed to the next page, an “Apply” branch to commit the current changes and continue and a “Cancel” branch to abort the execution of the current virtual application. Therefore, Fabasoft app.ducx provides special keywords for these common cases in form of predefined branches:

  • nextbranch
    “Next” branch for proceeding to the next dialog and continuing the execution of a virtual application. This branch is implicitly assigned the caption string COODESK@1.1:StrNext and up to version18 the symbol COODESK@1.1:SymbolNext. Moreover, the leave and the default branch modifier are also automatically assigned to a nextbranch.
  • applybranch
    An “Apply” branch for committing the current changes and continuing the execution of a virtual application. This branch is implicitly assigned caption COODESK@1.1:StringApply. Moreover, the default branch expression coouser.CommitRoot(true) is implicitly assigned to an applybranch.
  • cancelbranch
    A “Cancel” branch for aborting the execution of a virtual application. This branch is set to ignore any user input and is implicitly assigned the caption string COODESK@1.1:StringCancel and up to version 18  the symbol COODESK@1.1:SymbolCancel. Moreover, the default branch expression coouser.Cancel() is implicitly assigned to a cancelbranch.

The default branch handler expressions, captions, and symbols of nextbranch, applybranch and cancelbranch can be overridden using the expression, caption, and symbol keywords.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCVAPP@1.1001;
  import FSCVENV@1.1001;

  menu usecase EditInvoice on selected {
    variant Order {
      application {
        boolean invoiceprinted;
        Invoice invoice;
        expression {
          invoice = cooobj.orderinvoice;
          if (invoice != null) {
            invoiceprinted = false;
            invoice.ObjectLock(true, true);
            ->EditInvoiceDialog;
            if (invoiceprinted) {
              invoice->MailInvoice();
            }
          }
          else {
            throw #NoInvoiceFound;
          }
        }
        dialog EditInvoiceDialog {
          form = PageInvoice;
          target = invoice;
          cancelbranch;
          applybranch;
          branch printinvoice {
            symbol = SymbolPrinter;
            caption = StrPrintInvoice;
            visible = expression {
              invoice.CheckGetAccess(cootx, #content)
            }
            expression {
              try {
                invoice->PrintInvoice();
                invoiceprinted = true;
              }
              catch (@error) {
                if (coort.GetErrorMessage(@error) != #COOSTERR_CANCEL) {
                  throw @error;
                }
              }
            }
          }
          nextbranch;
          behaviors {
            required {
              applytofield = "objname";
            }
            
editcondition {
              
condition = expression {
                Invoice invoice;
                
invoice.invoicepaymentdate == null
              }
              applytofield = "invoicestate";
            }
            required, editcondition, create {
              
condition = expression{
                
coort.GetCurrentUserRolePosition() == #SysAdm
              }
              applytofield = "invoiceorder";
            }
          }
        }
      }
    }
  }
}

Extending a DialogPermanent link for this heading

Use the using keyword followed by the reference of the generated virtual application (for the combination of variant and use case) and curly braces. The reference is formed by concatenating the variant name and the use case name. The virtual application contains the dialog that should be extended. To extend a dialog with a branch, use the extend dialog keywords followed by the dialog reference and curly braces.

Syntax

using <reference of variant><reference of use case> {
  extend dialog reference {
    ...
  }
}

Note: The extension of dialogs that belong to another non-friend software component is not allowed.

In the following example the “EditInvoiceDialog” of the menu use case “EditInvoice” that is defined for variant “Order” gets extended by a cancel branch.

Example

using OrderEditInvoice {
  extend dialog EditInvoiceDialog {
    cancelbranch;
  }
}

Implementing a Stand-Alone Virtual ApplicationPermanent link for this heading

By using the keyword application, it is possible to create a virtual application without use case. All features described for applications used as implementations for use cases are available. This is implemented for backward compatibility and generally should not be used.

Example

application ShowUserSettings () {
  epression {
    UserEnvironment ue = cooenv;
    EditObjectAttributesApp(ue);
  }
}

Overriding an Existing Use Case ImplementationPermanent link for this heading

Syntax

override usecase {
  variant objectclass {
    impl = ...
  }
}

You can override an existing use case implementation for the object classes belonging to your software component.

When overriding an existing use case implementation, the override keyword must precede the reference of the use case that you want to override, followed by curly braces.

Note: Overriding an external use case from a non-friend component for an external class from a non-friend component is forbidden.

In the following example, a custom implementation is defined for COOSYSTEM@1.1:AttrContentSet in object class APPDUCXSAMPLE@200.200:Invoice.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  override AttrContentSet {
    variant Invoice {
      expression {
        // Call super method
        cooobj.CallMethod(cootx, coometh);
        if (attrdef == #content) {
          // Initialize invoice approval process
          cooobj.COOWF@1.1:InitializeWorkFlow([#APPDUCXSAMPLE@200.200:
            ProcInvoiceApproval]);
        }
      }
    }
  }
}

Implementing TriggersPermanent link for this heading

There are two types of triggers:

  • object-level triggers fired for specific events involving objects
  • property-level triggers fired for specific events involving properties

Triggers are executed in the background and therefore cannot involve user interaction. Furthermore, please note that triggers must not be implemented as virtual applications.

Note: Triggers should be defined as actions. Omit the usecase keyword when defining new triggers using the app.ducx use case language.

Object-Level TriggersPermanent link for this heading

Object-level triggers are defined in object class COOSYSTEM@1.1:Object and are invoked automatically.

You can override object-level triggers for your object classes to change or add to their default behavior.

When overriding an object-level trigger, the fully qualified reference of the trigger action must be denoted, followed by curly braces.

The implementation of an overridden object-level trigger should usually call the super method. In app.ducx expression language, this can be accomplished by the following statement:

cooobj.CallMethod(cootx, coometh);

Constructor TriggerPermanent link for this heading

The object constructor trigger COOSYSTEM@1.1:ObjectConstructor is fired when a new instance of an object class is created. The trigger is invoked on the new instance.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  override ObjectConstructor {
    variant Order {
      expression {
        // Call super method
        cooobj.CallMethod(cootx, coometh);
        // Add order to the list of orders stored in the current user's
        // desk object (expecting that APPDUCXSAMPLE@200.200:userorders has

        // been added as a property of the desk object class)

        root = cooroot;
        if (root.HasAttribute(cootx, #userorders)) {
          cooobj.COODESK@1.1:ShareObject(null, null, #userorders, root);
        }
      }
    }
  }
}

Prepare Commit TriggerPermanent link for this heading

The prepare commit trigger COOSYSTEM@1.1:ObjectPrepareCommit is fired before any set triggers are invoked. The trigger is invoked on the object that is to be changed.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  override ObjectPrepareCommit {
    variant Order {
      expression {
        // Generate the order's name based on the name build configuration
        cooobj.FSCCONFIG@1.1001:ObjectPrepareCommitNameBuild();
      }
    }
  }
}

Finalize Commit TriggerPermanent link for this heading

The finalize commit trigger COOSYSTEM@1.1:ObjectFinalizeCommit is fired after the set triggers have been invoked, but before any changes are committed to an object. The trigger is invoked on the object that is to be changed.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  override ObjectFinalizeCommit {
    variant Order {
      expression {
        // Call super method
        cooobj.CallMethod(cootx, coometh);
        // Enforce that users also have to change the list of order
        // positions when changing the vendor

        if (cootx.IsAttributeChanged(cooobj, #ordervendor)
            && !cootx.IsAttributeChanged(cooobj, #orderpositions) {
          throw #InvalidChange;
        }
      }
    }
  }
}

Property-Level TriggersPermanent link for this heading

Property-level triggers are fired only when they are explicitly attached to a property for a predefined trigger event.

For defining a property-level trigger, you have to follow these steps:

  • Create and implement a use case for the trigger.
  • Attach the trigger action to the trigger event in the property definition.

For further information on how to attach a trigger action to a trigger event in a property definition, please refer to chapter “Assigning Triggers to a Property”.

Constructor TriggerPermanent link for this heading

When a new instance of an object class is created, the constructor triggers of all properties are fired – provided that a constructor trigger has been defined for the particular property.

For a constructor trigger action, you have to assign the COOSYSTEM@1.1:AttrConstructorPrototype. The initialization value that is to be assigned to the property must be returned in the second parameter.

Example

app.ducx Object Model Language

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    OrderState orderstate readonly(ui) {
      ctor = OrderStateCtor;
    }
  }
}

app.ducx Use Case Language

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  OrderStateCtor(parameters as AttrConstructorPrototype) {
    variant Order {
      expression {
        // Initialize order state as pending
        value = "OS_PENDING";
      }
    }
  }
}

Get Value TriggerPermanent link for this heading

The get value trigger is fired, after the property value is read from the database.

For a get value trigger action, you have to assign the COOSYSTEM@1.1:AttrGetPrototype. The value read from the database is passed to the trigger in the second parameter. The implementation can then modify the value, and return the modified value – also in the second parameter.

Instead of defining a constraint, the example shown can also be implemented using a get value trigger as illustrated in the following example.

Example

app.ducx Object Model Language

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    currency ordertotal readonly volatile {
      get = GetOrderTotal;
    }
  }
}

app.ducx Use Case Language

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  GetOrderTotal(parameters as AttrGetPrototype) {
    variant Order {
      expression {
        for (OrderPosition position : cooobj.orderpositions) {
          Product product = position.product;
          if (product != null) {
            currency total += product.unitprice * position.quantity;
          }
        }
        total;
      }
    }
  }
}

Set Value TriggerPermanent link for this heading

The set value trigger is fired, before the property value is written to the database.

For a set value trigger action, you have to assign the COOSYSTEM@1.1:AttrSetPrototype. The current value of the property is passed to the trigger in the second parameter. The value contained in the property before it was changed is made available in the third parameter. If the value to be written to the database is changed by the implementation of the trigger, it must be returned in the second parameter.

Example

app.ducx Object Model Language

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class<ContentObjectClass> : ContentObject {
    date invoicepaymentdate {
      set = SetInvoiceDate;
    }
  }
}

app.ducx Use Case Language

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  SetInvoiceDate(parameters as AttrSetPrototype) {
    variant Invoice {
      expression {
        if (value != null && value < coonow) {
          cooobj.ObjectLock(true, true);
          cooobj.invoicestate = "IS_PAID";
        }
      }
    }
  }
}