...

Виджет «Динамический список» для запуска процесса по выбранным элементам списка

Тема в разделе "Примеры решений и дополнительных модулей", создана пользователем fedorova, 11 фев 2022.

  1. fedorova

    fedorova Участник

    Цель:
    в раздел «Платежи», загруженный из ELMA365 Store, добавить страницу «Платежи на сегодня», на которой специалист по платежам (казначей) будет видеть все предстоящие платежи (из приложения «Заявки на расход ДС») на сегодня, сгруппированные по статусу, и иметь возможность вернуть отдельные платежи на корректировку либо выбрать несколько платежей и перенести их на другой день.

    Решение:
    1) Создать в разделе «Платежи» страницу «Платежи на сегодня» и разместить там виджет «Динамический список», в него будет передаваться таблица тех статусов, в которых находятся сегодняшние платежи – для создания подзаголовков таблицы с платежами.
    2) В виджет «Динамический список» поместить пользовательский виджет «Платежи в статусе» для отображения только тех платежей, которые находятся в соответствующем статусе Динамического списка из п.1.
    Добавить в пользовательский виджет еще один «Динамический список», в который будет передаваться таблица платежей.
    3) В этот (второй) Динамический список добавить еще один пользовательский виджет «Данные по заявке», в котором будут отображаться данные по заявке на платеж.
    Вот так эта страница выглядит в готовом виде:
    [​IMG]
    Для удобства можно свернуть панель с платежами в каком-либо из статусов (на рисунке выше свернуты платежи в статусе «Согласовано»).


    Шаги реализации решения:
    Схематично изобразим эту матрешку:
    [​IMG]
    На рисунке цифрами обозначены передаваемые данные из одного виджета в другой (их описание будет дальше).


    Процесс
    Для начала в приложении «Заявки на расход ДС» создадим процесс для переноса платежей на другую дату. Так его и назовем, «Перенести платежи» (код процесса «shift_payments»).


    Страница «Платежи на сегодня»
    1. Создадим в разделе «Платежи» страницу «Платежи на сегодня». Перейдем в конструктор и откроем вкладку «Контекст».
    2. Создадим в контексте формы следующие свойства:
    [​IMG]

    В настройках Таблицы статусов создадим только одну колонку типа «Данные», тип данных «Строка», имя свойства «Статус платежа», код «payment_status».

    Перейдем на вкладку «Сценарии» и добавим следующий код:
    Код:
    
    declare const alertany;
    declare const 
    consoleany;

    async function onInit() {                                                                                               // Объявим функцию, выполняемую при инициализации формы
    // Заполним таблицу "Таблица статусов" из контекста формы для генерации динамического списка, и затем посчитаем итоговую сумм
        
    Context.data.shifted_payments = {};                                                                                 // В переменной произвольного типа создадим пустой объект, куда затем будем записывать идентификаторы платежей, которые необходимо перенести на другой день

        
    const today = new TDate();
        const 
    today_payments await Namespace.app.payment_request.search()                                                 // С помощью методов search().where() найдем сегодняшние платежи и с помощью метода all() получим данные этих элементов
            
    .where((fg) => g.and(
                
    // f.payment_desired_date.eq(today),
                
    f.__deletedAt.eq(null),
            ))
            .
    size(10000)
            .
    all();

        if (
    today_payments && today_payments.length 0) {
            
    Context.data.payments today_payments;                                                                         // Все найденные платежи запишем в контекстную переменную

            
    let statuses_array: {indexnumbernamestring}[] = [];                                                       // Объявим массив объектов для записи номера и названия статуса

            
    for (let payment of today_payments) {
                if (
    statuses_array && statuses_array.length 0) {                                                          // Из каждого платежа на сегодня запишем статус в объект уникальных значений со свойствами "index" (порядковый номер статуса) и "name" (название статуса)
                    
    if (statuses_array.some(=> i.index === payment.data.__status!.index) === false) {
                        
    statuses_array.push({ indexpayment.data.__status!.indexnamepayment.data.__status!.name });
                    }
                } else {
                    
    statuses_array.push({ indexpayment.data.__status!.indexnamepayment.data.__status!.name });
                }
            }

            
    statuses_array.sort((ab) => a.index b.index : -1);                                                      // Отсортируем полученные объекты по порядковому номеру статуса

            
    for (let item of statuses_array) {                                                                              // Запишем названия статусов в Таблицу статусов из контекста формы
                
    const row Context.data.statuses_table!.insert();
                
    row.payment_status item.name;
            }
            
    Context.data.statuses_table Context.data.statuses_table;                                                      // Сохраним изменения в таблице статусов
            
    let total_sum today_payments.reduce((sumcurrent) => sum + (current.data.payment_amount current.data.payment_amount.asFloat() : 0), 0);       // Посчитаем общую сумму платежей на сегодня
            
    let sum_match total_sum.toString().match(/^(.*?)((?:[,.]\d+)?|)$/);                                           // Дальнейшие преобразования - для красивого отображения суммы в строке текста
            
    let sum_string '';
            if (
    sum_match) {
                
    sum_string sum_match[1].replace(/\B(?=(?:\d{3})*$)/g' ') + sum_match[2];
            } else {
                
    sum_string total_sum.toString();
            }
            
    Context.data.total = `Платежей на сегодня итого на сумму: ${sum_string} руб.`;
        } else {
            
    Context.data.total = `На сегодня платежи не запланированы.`;
        }
    }

    async function shift(): Promise<void> {                                                                                // Объявим функцию для переноса платежей на другой день
        
    const payments_id_list Context.data.shifted_payments;
        const 
    payments_to_be_shifted_idsstring[] = [];
        for (
    let id in payments_id_list) {                                                                                 // Запишем массив id тех платежей, которые нужно перенести на другой день
            
    payments_to_be_shifted_ids.push(payments_id_list[id])
        }
        
    console.log(payments_to_be_shifted_ids);
        if (
    payments_to_be_shifted_ids.length === 0) {                                                                     // Выведем на экран сообщение об отсутствии выбранных платежей
            
    alert('Не выбраны платежи');
            return;
        }
        if (!
    Context.data.new_payment_date) {
            
    alert('Не указана новая дата оплаты');                                                                         // Выведем на экран сообщение о том, что не указана дата, на которую нужно перенести платеж
            
    return;
        }
        const 
    new_date Context.data.new_payment_date.asDatetime(new TTime(0000));                                  // Преобразуем новую дату платежа в формат TDatetime
        
    if (payments_to_be_shifted_ids && payments_to_be_shifted_ids.length && Context.data.new_payment_date) {        // Запустим процесс переноса платежей на другой день. Передадим в процесс по переносу платежей id выбранных элементов и новую дату платежа
            
    let inputDataProcess$payment$payment_request$shift_payments$Context$$Data = {
                
    payment_date: (new_date.format() as any) as TDate,
                
    paymentspayments_to_be_shifted_ids.map((itemany) => ((item as any) as ApplicationItemRef<Application$payment$payment_request$DataApplication$payment$payment_request$Params>)),
            }
            
    await Context.fields.payments.app.processes.shift_payments.run(inputData);
        }
    }
    На вкладку «Шаблон» вынесем свойство «Новая дата платежа».

    Добавим виджет «Кнопка», назовем ее «Перенести дату». Тип действия выберем «Сценарий», и в «Событии при нажатии» выберем функцию «shift», чтобы при нажатии на кнопку запускался скрипт запуска процесса «Перенести платежи» по выбранным платежам.

    Первый динамический список - группировка
    Добавим на страницу виджет «Панель с заголовком», заголовок можно не указывать – поставить пробел. В Этот виджет положим виджет «Динамический список» (собственно, панель с заголовком нам была нужна для того, чтобы динамический список выглядел красиво).
    В настройках динамического списка в качестве списочных данных выберем таблицу из контекста формы «Таблица статусов».
    Ниже динамического списка разместим виджет «Надпись», в котором текст свяжем с переменной «Платежей на сегодня итого на сумму:». Итоговая сумма будет вычисляться в сценарии, в функции onInit(), которая выполняется при открытии страницы.
    Теперь сохраним все изменения в конструкторе страницы и вернемся в раздел «Платежи».

    Второй динамический список
    В настройках раздела «Платежи» (шестеренка) выберем пункт выпадающего меню «Интерфейсы» и создадим пользовательский виджет. Назовем его «Платежи в статусе».
    Откроем вкладку «Контекст» и создадим переменные с такими настройками:
    [​IMG]
    Продолжение статьи см.в комментариях.

    Вложения:

    Последнее редактирование модератором: 27 янв 2023
  2. fedorova

    fedorova Участник

    Настройки таблицы «Платежи»:
    [​IMG]
    Эта таблица – для формирования динамического списка – и записи данных по каждому платежу в данном статусе. Настройки свойств таблицы должны соответствовать настройкам свойств приложения, данные по элементу которого будут отображаться в динамическом списке (в данном случае – приложения «Заявки на расход ДС»).

    На вкладке «Сценарий» добавим код по заполнению таблицы:
    Код:
    
    async function onInit(): Promise<void> {
        if (
    Context.data.payments && Context.data.payments.length 0) {
            const 
    payments await Promise.all(Context.data.payments.map(async i => i.fetch()));
            const 
    payments_filtered payments.filter(=> i.data.__status!.name === Context.data.status);
            for (
    let i 0payments_filtered.lengthi++) {
                const 
    row Context.data.payments_table!.insert();
                
    row.payment payments_filtered[i];
                if (
    payments_filtered[i].data.payee_employee) {
                    
    row.payee_employee payments_filtered[i].data.payee_employee!;
                }
                if (
    payments_filtered[i].data.payee_partner) {
                    
    row.payee_partner payments_filtered[i].data.payee_partner!;
                }
                if (
    payments_filtered[i].data.payment_aim) {
                    
    row.payment_aim payments_filtered[i].data.payment_aim!;
                }
                if (
    payments_filtered[i].data.payment_amount) {
                    
    row.payment_amount payments_filtered[i].data.payment_amount!;
                }
                if (
    payments_filtered[i].data.payment_request_comment) {
                    
    row.payment_request_comment payments_filtered[i].data.payment_request_comment!;
                }
                if (
    payments_filtered[i].data.payment_request_date) {
                    
    row.payment_request_date payments_filtered[i].data.payment_request_date!;
                }
            }
            
    Context.data.payments_table Context.data.payments_table;
        }
    }
    Отмечу, что с точки зрения быстродействия системы этот вариант не лучший, т.к. для платежей в каждом статусе мы будем получать данные при помощи функции fetch(). Зато это наименее трудозатратный способ))). О том, как сделать правильно, напишу в конце статьи.
    Перейдем на вкладку «Шаблон». Здесь добавим виджет «Панель с заголовком», текст заголовка увяжем с переменной «Статус».
    В панель с заголовком поместим переменную «Перенести все». При нажатии на эту галку все платежи в данном статусе будут выбраны для запуска процесса по переносу даты.
    Еще в панель с заголовком добавим виджет «Динамический список». Списочные данные для него будем получать из переменной «Таблица платежей». В виджет «Динамический список» можно добавлять различные виджеты («Колонки», «Строка», «Кнопка», «Надпись»), с помощью которых можно отразить информацию по элементу динамического списка, но если нужны ссылки на элементы приложения или пользователей, в динамический список следует поместить еще один пользовательский виджет.
    Сохраняем изменения и переходим к самой маленькой матрешке.

    Отображение данных по элементу динамического списка
    Снова в интерфейсах раздела «Платежи» создадим пользовательский виджет, назовем его «Данные по заявке».
    В контексте виджета создадим свойства:

    Настройки этих свойств должны соответствовать настройкам свойств приложения «Заявки на расход ДС».
    В настройках свойств «Получатель платежа (контрагент)» также установим условие отображения: «Получатель платежа (контрагент)» - Не пустое значение.
    Аналогично настроим свойство «Получатель платежа (сотрудник)».
    Вынесем все свойства, кроме «Заявки на смену даты платежа», на вкладку «Шаблон». Все свойства – только для чтения, кроме «Перенести».
    Разместим их при помощи виджета «Колонки»
    Добавим также кнопку «На корректировку», отобразим ее в виде ссылки. Тип действия – Запустить процесс «Согласование расходования денежных средств», входные параметры для процесса – «Заявка на расход ДС». При нажатии на эту кнопку на странице будет запускаться процесс согласования платежа.

    В настройках свойства «Перенести» на вкладке «События» добавим событие при изменении значения этого свойства. Назовем его «onChangeShiftPayment».
    На вкладке «Сценарии» напишем эту функцию, которая будет выполняться при выборе платежа – а именно в переменную «Заявки на смену даты платежа» будет записываться id выбранного элемента (либо при снятии галки id элемента будет удаляться):
    Код:
    
    async function onChangeShiftPayment(): Promise<void> {
        const 
    payment Context.data.payment;
        
    let shifted_payments Context.data.shifted_payments;

        if (
    Context.data.change_date && payment && payment.id) {
            
    shifted_payments[payment.id] = payment.id;
           

            
    let id '';
            for (
    let payment in shifted_payments) {
                
    id = (id id ', ' '') + payment.valueOf();
            }
            
    console.info(`ok ${shifted_payments[0]} + ${id}`)

        } else {
            if (
    payment) {
                
    delete shifted_payments[payment.id];
               
                
    let id '';
                for (
    let payment in shifted_payments) {
                    
    id = (id id ', ' '') + payment.valueOf()
                }
                
    console.info(`deleted ${shifted_payments[0]} + ${id}`)

               
            }
        }
    }
    Опубликуем виджет «Данные по заявке».

    Соберем матрешки
    Теперь вернемся в конструктор пользовательского виджета «Платежи в статусе» и поместим в виджет «Динамический список» пользовательский виджет «Данные по заявке». Передадим в него данные по заявке из динамического списка:


    Опубликуем виджет «Платежи в статусе».

    Перейдем в конструктор страницы «Платежи на сегодня». В виджет «Динамический список» поместим пользовательский виджет «Платежи в статусе». Передадим в него следующие параметры:

    Опубликуем страницу.

    НАКОНЕЦ-ТО ГОТОВО!


    Варианты улучшения решения:
    1) Добавить в шапке страницы слайдер по рабочим дням, чтобы можно было посмотреть плановые платежи на завтра-послезавтра;
    2) Добавить редактируемые поля в виджете «Данные по заявке», при изменении которых данные будут сохраняться в самой заявке;
    3) Хранить id выбранных элементов в storage раздела «Платежи» или приложения «Заявки на расход ДС», чтобы при переключении на другой раздел или вкладку эта информация сохранялась (чтобы галки не слетали);
    4) Для более быстрой загрузки страницы все данные по платежам на сегодня сохранять в таблицу в контексте страницы «Платежи на сегодня». Точно такую же таблицу создать в контексте виджета «Платежи в статусе» и передавать из таблицы страницы в таблицу виджета. Затем в функции onInit() виджета проходиться по каждой строке этой таблицы и если статус платежа соответствует статусу из поля «Статус», копировать все данные в таблицу «Платежи».
  3. yslebedeva

    yslebedeva Новичок

    Добрый день! А каким образом передаются данные из динамического списка в пользовательский виджет?
  4. fedorova

    fedorova Участник

    Добрый день! Данные из динамического списка передаются в пользовательский виджет в настройках пользовательского виджета (чтобы зайти в настройки, нужно нажать на шестеренку в правом верхнем углу виджета) - с помощью простого маппинга полей.

    Для выбора данных именно из динамического списка нужно в выпадающем меню со списком полей выбрать пункт "Элемент списка строки" - с черным кружочком.

    Вложения:

  5. grylev

    grylev Участник

    Добрый день! Если в "источнике" (форма с которой был вызван виджет) произойдет изменение Контекста, виджет обновиться автоматический или он выполняется только пи загрузке всей формы?
  6. fedorova

    fedorova Участник

    Добрый день! Виджет "Динамический список" обновляется на лету
  7. o.prokopenkova

    o.prokopenkova Новичок

    Здравствуйте. Спасибо за полезную статью. Но, мне кажется, что во второй части не хватает нескольких скриншотов. Добавьте, пожалуйста.
    [​IMG]

    [​IMG]
  8. Anastasiya

    Anastasiya Новичок

    Добрый день! Добавьте недостающие скриншоты в статью