Рассмотрим хранение данных контекста формы, как одно из применений Произвольного типа.
При работе с данными на форме, в случае, когда запросы на получение данных выполняются с сервера, а взаимодействие с полученными данными на клиенте/сервере (например, ставится задача предоставить возможность пользователю взаимодействовать с данными с ограниченным доступом), может возникнуть потребность сократить количество запросов на получение данных и/или хранить данные произвольного формата и не множить контекст формы.
Для решения данной задачи возможно использовать Произвольный тип.
Например, необходимо сформировать отчёт по работе пользователей. Решение сводится к запросу на получение и обработку данных на сервере, формированию таблицы и применению фильтров на клиенте.
Оперативные данные хранятся в формате JSON и записываются в свойство типа Произвольный тип.
Для работы с оперативными данными реализован класс FormCache с заданным набором полей и методом для конвертации данных в JSON.
	Код:
	
/**
* Данные по категории
*/
type EnumDataItem = {
    item: ApplicationItemRef<any, any> | UserGroupItemRef;
    enum_item: TEnum<string>;
}
/**
* Кэш формы
*/
class FormCache {
    /**
     * Менеджеры
     *
     * @remarks На клиент в кэше будет передаваться информация по полученным пользователям.
     * При наличии требований конфиденциальности возможно оставить только RefItem, и на фильтрации каждый раз получать пользователей по RefItem
     * Либо оставлять только необходимые данные в json
     */
    managers: UserItem[];
    /**
     * Данные по категории Роль
     */
    role_enum_data: EnumDataItem[];
    constructor() {
        this.managers = [];
        this.role_enum_data = [];
    }
    json() {
        return {
            managers: this.managers.map(item => ({
                id: item.id,
                code: item.code,
                namespace: item.namespace,
                data: (item as any).json()
            })),
            role_enum_data: this.role_enum_data.map(data_item => ({
                item: {
                    id: data_item.item.id,
                    code: data_item.item.code,
                    namespace: data_item.item.namespace
                },
                enum_item: {
                    code: data_item.enum_item.code,
                    name: data_item.enum_item.name
                }
            }))
        }
    }
}
/**
* Размер пакета промисов (оптимальное значение)
*/
const promises_package_size = 20;
 Получение данных для фильтрации по группам пользователей и подчинённым менеджерам с записью в form_cache.
	Код:
	
/**
* Подготовить данные перед заполнением
*/
async function getDataReady(): Promise<void> {
    // Группы компании
    const user_group_codes = ['018e9932-69e4-7e20-86fd-cdfb34f17eab', '018e3277-23ff-7029-9dea-704587a999c9'];
    const company_groups = await System.userGroups.search()
        .where((f, g) => g.and(
            f.__deletedAt.eq(null),
            f.namespace.eq('_clients._companies'),
            f.code.in(user_group_codes)
        ))
        .size(100)
        .all();
    if (company_groups.length < 0) {
        throw new Error(`company_groups is empty at ${getDataReady.name}`);
    }
    const role_enum_data: EnumDataItem[] = [];
    for (let company_group of company_groups) {
        const enum_item: TEnum<string> = {
            code: company_group.id,
            name: company_group.data.__name
        };
        role_enum_data.push({
            item: company_group,
            enum_item: enum_item
        });
        Context.fields.role_enum.data.variants.push(enum_item);
    }
    const company_groups_users = await System.users.search()
        .where((f, g) => g.and(
            f.__deletedAt.eq(null),
            f.__status.eq(UserStatus.Active),
            g.or(...company_groups.map(group => f.groupIds.has(group)))
        ))
        .size(10000)
        .all();
    if (company_groups_users.length < 0) {
        throw new Error(`company_groups_users is empty at ${getDataReady.name}`);
    }
    const current_user = await System.users.getCurrentUser();
    // Элементы оргструктуры текущего пользователя
    let current_user_child_orgstructure_items: OrganisationStructureItem[] = [];
    if (current_user.data.osIds && current_user.data.osIds.length > 0) {
        current_user_child_orgstructure_items = await System.organisationStructure.search()
            .where((f, g) => g.and(
                f.__deletedAt.eq(null),
                f.parent.in(current_user.data.osIds!.map(item => item.id))
            ))
            .size(1000)
            .all();
    }
    const orgstructure_items: OrganisationStructureItem[] = [];
    // Асинхронное получение поддеревьев по подчиненным элементам оргструктуры текущего пользователя
    await Promise.all(
        current_user_child_orgstructure_items.map(async item => orgstructure_items.push(...await allLevelSubItems(item)))
    );
    // Пересечение пользователей групп и пользователей по подчиненным должностям текущего пользователя, включая текущего пользователя
    const users_intersection = company_groups_users.filter(user =>
        user.id === current_user.id ||
        orgstructure_items.some(item => user.data.osIds?.some(os_item => os_item.id === item.id))
    );
    // Запись в кэш
    const form_cache = new FormCache();
    form_cache.managers = users_intersection;
    form_cache.role_enum_data = role_enum_data;
    Context.data.form_cache = form_cache.json();
    Context.fields.managers.data.setFilter((f, c, g) => g.and(
        f.__id.in(form_cache.managers.map(item => item.id))
    ));
}
/**
* Получение элементов дерева
*
* @param item Элемент оргструктуры
*
* @returns Элементы дерева оргструктуры
*/
async function allLevelSubItems(item: OrganisationStructureItem): Promise<OrganisationStructureItem[]> {
    let items = [item];
    const child_items = item.getChildren();
    for (const child_item of child_items) {
        items = items.concat(await allLevelSubItems(child_item));
    }
    return items;
}
 Формирование таблицы с заданной фильтрацией.
	Код:
	
/**
* Заполнить таблицу с данными
*/
async function writeDataTable(): Promise<void> {
    if (!Context.data.form_cache) {
        throw new Error(`Context.data.form_cache is falsy at ${writeDataTable.name}`);
    }
    const form_cache: FormCache = Context.data.form_cache;
    if (!form_cache || !form_cache.managers) {
        throw new Error(`form_cache is falsy at ${writeDataTable.name}`);
    }
    const data_table = Context.data.data_table!;
    // Очистка таблицы
    for (let i = data_table.length - 1; i >= 0; i--) {
        data_table.delete(i);
    }
    // Заполнение таблицы
    const managers_promises = form_cache.managers.map(async (user) => {
        // Фильтр на Менеджеров
        if (Context.data.managers && Context.data.managers.length > 0 && !Context.data.managers.some(item => item.id == user.id)) {
            return;
        }
        // Фильтр на Роль
        if (Context.data.role_enum && Context.data.role_enum.length > 0 &&
            !Context.data.role_enum.some(
                item => user.data.groupIds?.some(group_id => (group_id as any) === form_cache.role_enum_data.find(item_data => item_data.enum_item.code === item.code)?.item.id)
            )) {
            return;
        }
        const [deals_count, contacts_count] = await Promise.all([
            Context.fields.deals.app.search()
                .where((f, g) => g.and(                  
                    f.__deletedAt.eq(null),
                    f.__createdBy.eq(user),
                })
                .count(),
            Context.fields.contacts.app.search()
                .where((f, g) => g.and(                  
                    f.__deletedAt.eq(null),
                    f.__createdBy.eq(user),
                })
                .count()
        ]);
        const table_row = data_table.insert();
        table_row.manager = user;
        table_row.deals_amount = deals_count;
        table_row.contacts_amount = contacts_count;
    });
    // Ожидание промисов по пакетам в соответствии с заданным размером пакета
    for (let i = 0; i < managers_promises.length; i += promises_package_size) {
        await Promise.all(managers_promises.slice(i, i + promises_package_size));
    }
    Context.data.data_table = data_table;
}