Manage apps

You can access an app in several ways:

Create an app

You can create new app items in apps. For example, if the process context contains a variable of the App type order, you can create a new item of this app as follows:

const order = Context.fields.order.app.create();
order.data.client = Context.data.client;
await order.save();
Context.data.order = order;

Get an app using a link

If the context of a process, app, or widget contains an App type field, its value will contain a link to this object. To get the object itself, request it using the [[ApplicationItemRef.fetch]] method:

const order = await Context.data.order.fetch();

If the field stores multiple values, you can request all associated objects using the ApplicationField.fetchAll method:

const orders = await Context.fields.orders.fetchAll();

In the same way, you can use an app item that references another app to get the app items it has links to:

const order = await Context.data.order.fetch();
const client = await order.data.client.fetch();

Get the current status of an app item

You can get the current status of an app item in the following way:

const currentStatus = item.data.__status;
Context.data.__comment = `Current status: ${ currentStatus.name }`;

You can change an app item’s status using the [[ApplicationItem.setStatus]] method.

Search

To search for an item in a collection, use the Search object that you can get from the app’s description:

const pinnedClients = await Context.fields.client.app.search()
    .where(f => f.agent.eq(Context.data.__createdBy))
    .all();

With the Search object, you can request:

For a query divided into pages, use the following methods:

  • Search.from to define the number of items to skip from the start.
  • Search.size to define the number of results on a page (the maximum value is 10,000, the default is 10).
  • Search.sort to determine how results need to be sorted.

You can set a filter using Search.where. The filter is a closure with two arguments:

  • f is the set of filters to define conditions for specific object fields.
  • g represents global filter functions for full-text search and for complex filters GlobalFilters.

For example, if you want to select orders of a certain customer with the order name starting with “ab” that were created less than two weeks ago, here is the request that you will need to use:

const weekAgo = (new Datetime()).addDate(0, 0, -7);
const orders = await Application.search()
    .where((f, g) => g.and(
        f.client.eq(Context.data.client),
        f.__name.like('ab%'),
        f.__createdAt.gt(weekAgo),
    ))
    .all();

If you want to search orders by status (for example, to find all orders with the done status), use the following request:

const doneStatus = Application.fields.__status.variants.done;
const orders = await Application.search()
     .where(f => f.__status.eq(doneStatus))
     .all();

To get app items that refer to items of another app, use the following request:

const clients = await Application.search()
     .where(f => f.client.link(Context.data.order)
     .all();

The f object is created based on the descriptions of the app’s fields. For fields with multiple values, FieldArrayOperand is formed. For fields that expand (TString, TPhone, TEmail, TFullName, TLink and TEnum), StringFieldOperand is formed. For the rest, FieldOperand.

Filtering for App type fields

If you need to limit options available in an App type field, you can use the StaticApplicationFieldData.setFilter method:

 Context.fields.cars.data.setFilter((f, c, g) => g.and(
     f.active.eq(true),
     f.year_of_issue.gte(c.year)
 ));;

A filter is a closure to which three arguments are passed. The first f and the third g are similar to the ones passed to the closure via the Search.where method described above. The second argument c is a set of operands used to get a value from the context.

A filter is applied to the field itself; it is not only used on a certain form or in a business process. So, if a filter is not needed any longer, you can clear it using the StaticApplicationFieldData.clearFilter method:

Context.fields.cars.data.clearFilter();

Edit an app item

When you get an app item, you can change the values of its fields. After that you need to call the [[ApplicationItem.save]] method to apply the changes:

const order = await Context.data.order.fetch();
// Apply a discount
order.data.total = order.data.total.multiply(0.75);
await order.save();

Deleting an app item

To delete app items, the [[ApplicationItem.delete]] method is used. It marks the item as deleted (by filling in the Deleted on field, BaseItemData.__deletedAt). The item will still be available in search and accessible by link. Also, to delete an item, you don’t need to get the entire object, a link is enough:

await Context.data.order.delete();
Context.data.order = undefined;

Change the status of an app item

The status of an app item can be changed via the [[ApplicationItem.setStatus]] method:

// Changing the status of a recently created object that has not yet been saved
const request = await Context.data.request.fetch();
const finalStatus = request.fields.__status.variants.completed;
await request.setStatus(finalStatus, 'Closed automatically');

Start a business process

The Application object has access to the list of processes Application.processes. It can be used to start a process. Processes are started asynchronously, which means that the process will not yet be launched while the run function is being executed.

For example, you can launch a business process that manages service requests in a loop for all requests in the context:

for(let request of Context.data.service_requests) {
    await Application.processes.process_code.run({
        service_request: request
    });
}

Read more about working with processes in Process.

Get items from one collection or app in one request

Let’s say we obtained items of an app that has a Users type field, and we need to get the data of these users. Before optimization (N+1 requests will run consequentially):

const teams = await Namespace.app.komandy.search(...).all();
for (const team of teams) {
\t   const lead = await team.data.lead.fetch();
\t   const name = lead.data.__name;
\t   ...
}

After optimization (two requests will run consequently):

const teams = await Namespace.app.komandy.search(...).all();

// Get the users’ IDs
let teamLeadIds = teams.map(t => t.data.lead.id);

 // Delete duplicates of user IDs
teamLeadIds = teamLeadIds.filter(function(elem, index, self) {
    return index === self.indexOf(elem);
});

 // Get all users in one request
const users = await System.users.search().where(f => f.__id.in(teamLeadIds)).size(10000).all();

// Form an object of the following type: {user_ID: user_full_name}
const namesById: { [key: string]: string } = {};
for (const user of users) {
\t   namesById[user.id] = user.data.__name;
}