суббота, 26 января 2019 г.

Авансовый отчет на PowerApps и Dynamics 365 Customer Engagement

Предисловие

      В этой статье хотелось бы поделиться опытом применения PowerApps совместно с Dynamics 365 Customer Engagement (CRM) в компании AWARA-IT. В нашей компании всегда приветствуется проявление инициативы по новым, интересным направлениям в сфере IT. PowerApps на мой взгляд является одним из таких. Так же достаточно насущно стоит вопрос оперативности и простоты заполнения авансовых отчетов сотрудниками, которые часто ездят в командировки.
Начнем с того что такое PowerApps? Это инструмент для создания бизнес-приложений для веба и мобильных платформ. Подразумевается, что приложения будут тесно интегрироваться с облачными службами Microsoft и Office 365. Готовое приложение можно открывать по ссылке в браузере или же через специальное приложение на Android  или IOS.
Когда это полезно?
  1.       Когда вы хотите создать бизнес приложение, которое использует несколько источников данных (SharePoint Online, SQL Azure, Flow, Dynamics 365).
  2.       Когда нет жестких требований к красоте интерфейса и в приоритете функциональность. Хотя возможность редактирования стилей так же доступна.
  3.       Когда у вас есть подписка на Office 365.
  4.       Когда некому программировать под мобильные платформы и хочется получить готовый результат в короткие сроки.
  5.       Если вы хотите сэкономить, то можно построить решение на базе Common Data Services и PowerApps. При этом доступ к исходным системам необязателен и можно сэкономить на лицензиях.

Мне удалось создать достаточно функциональное приложение по управлению авансовыми отчетами примерно 3 недели, уделяя этому примерно 3 часа в день, с учетом недели обучения на YouTube каналах. Мне достаточно было нескольких уроков на этом канале. Если есть трудности, то можно задать вопрос на форуме. Отвечают достаточно оперативно.
Поскольку ресурсов для обучения предостаточно, то я не буду описывать каждый шаг в создании приложения, а укажу на ключевые моменты в создании приложения, которые вызвали у меня затруднения. За простоту разработки приложения приходится платить слабой информативностью ошибок. Иногда приходилось долго подбирать правильное поле для сохранения или отображения значений. И так поехали.

Авансовые отчеты 

Суть приложения в том, чтобы сотрудники могли создавать заявки на командировку, заносить расходы в процессе командировки и прикреплять фотографии чеков. Заполненный авансовый отчет отправлять в бухгалтерию.
Приложение будет адаптировано для мобильного приложения. В качестве хранилища будет использоваться облачная версия Dynamics 365 Customer Engagement (CRM). Рассылки уведомлений я не будут описывать в этой статье, лишь добавлю ссылки на те методы, которые использовал для формирования писем с вложениями.

Структура данных

         Схематично структуру данных можно представить в виде следующей схемы.
Для построения схемы пользовался этим.
В сущностях CRM это выглядит следующим образом. Несущественные поля упускаю.

Клиент (awr_client)



Проект (awr_project)

Авансовый отчет (awr_expensereport)

Включены примечания, к ним будут прикладываться фотографии.

Поле awr_name расширено до 450 символов

Строка авансового отчета (awr_expensereportrow)
Поле awr_name расширено до 450 символов

Списки авансовых отчетов (Отчетов)

Как мы ранее договорились, описывать создание приложения очень подробно я не буду. Если вы пропустили предисловие, то там говорилось о том, что можно быстро научиться создавать PowerApps приложения на YouTube, например тут.
Начало создания приложения выглядит так:





    Для начала зададим переменные для хранения почты текущего пользователя и статус для выбранного представления. Мы это будем использовать чуть позже. Фокус на базовом экране.

Set(varCurrentUserEmail;ПользователиOffice365.MyProfile().Mail);;
Set(varSelectedViewStatus;1)
    Для меня было странно, что переменные создаются в момент присвоения значений, а не перед этим. Я долго искал где их определить. Из-за этой странной логики, иногда переменные ведут себя странно.

   Не смотрите на цвета, это я уже раскрашивал списки после отладки.

Важно! Разделители между параметрами и методами зависят от локальных настроек. Подробнее тут

Добавим все необходимые источники данных.


Добавим раскрывающийся список с представлениями для списка.


Метод на изменение обновляет значение выбранного статуса


Set(varSelectedViewStatus;Switch(ddListView.Selected.Value;"Все мои авансовые отчеты";124000000;"Мои черновики";1))

Отфильтруем в зависимости от выбранного представления и по почте текущего пользователя.


Сортировка осуществляется по дате создания в обратном порядке, т.е. сначала самые новые.

SortByColumns(Search(Filter('Авансовые отчеты';Сотрудник = varCurrentUserEmail && ('Причина состояния'=1 || 'Причина состояния'=varSelectedViewStatus)); TextSearchBox1.Text; "awr_name"); "createdon"; If(SortDescending1; Ascending; Descending))

Макет я сначала выбрал с 3 значениями, потом просто скопировал еще одно поле и вывел туда значение общей суммы отчета.



Можете выбрать любой шаблон, какой захочется.

Цвет строк отчетов будет зависеть от статуса.


Переход на страницу просмотра определяем следующим образом.
Мы определяем новую переменную, чтобы туда записать выбранный отчет

Navigate(scrnViewExpenseReport; ScreenTransition.None;{selectedExpense:galExpenseList.Selected})


Форма просмотра отчета

На самой форме нужно поменять способ получения текста LookUp полей.

LookUp(Клиенты;awr_clientid=ThisItem.Клиент;awr_name)


LookUp(Проекты;awr_projectid=ThisItem.Проект; awr_name)

Определим для начала переходя на форму редактирования.


EditForm(frmEdit);;Navigate(scrnEditExpenseReport; ScreenTransition.None)

Форма редактирования


На форме редактирования самым сложным было настроить LookUp поля, чтобы они сохраняли данные. Что я для этого сделал?
Поменял настройку в приложении. После чего появилась возможность менять типы полей для LookUp значений. Я 2 дня пытался понять почему у меня ничего не меняется как написано в мануалах, пока не поменял это.


        При добавлении лукап полей важно выбрать именно текстовое поле, в котором показывается Guid. Может можно и как-то по-другому это настроить, но у меня и мануалах так.


Меняем тип на «Допустимые значения»



Акцент на выпадающем списке и задаём значения списка.

ShowColumns(Клиенты;"awr_name";"awr_clientid")


Теперь отображаемое текущее значение.



LookUp(Клиенты;awr_clientid=Parent.Default;awr_name)

Так же важно указать возвращаемое значение.

ddClient.Selected.awr_clientid



Такие же действия производим с проектами.


ShowColumns(Filter(Проекты; awr_client.awr_clientid=ddClient.Selected.awr_clientid);"awr_name";"awr_projectid")

Как видите значения фильтруются по выбранному клиенту.


LookUp(Проекты;awr_projectid=Parent.Default;awr_name)


Для поля даты начала ставим условие, что если мы создаем новый отчет, то ставить значение текущей даты.


If(frmEdit.Mode = FormMode.New; Today(); Parent.Default)

Дату окончания поставим больше на 3 дня по-умолчанию.


Кнопка сохранения


Кнопка отмены


Форма прикрепления файлов

Создадим новый экран, где у нас будут прикрепляться фотографии чеков и показываться список уже имеющихся


Создадим экран с камерой


Зададим действие нажатия на камеру для получения фотографии.


ClearCollect(CameraImages;Camera1.Photo);;
Clear(Image);;
ForAll(CameraImages;Collect(Image;{Filename:"img.jpg";fileBody:Url}));;
Back()

Здесь мне пришлось сначала написать Collect(Image;{Filename:"img.jpg";fileBody:Url}) , чтобы создалась переменная Image. Потом уже все расставить в нормальном порядке.
Переход на экран с камерой у нас будет происходить при нажатии на кнопку 


Просмотр сделанной фотографии.


И самое интересное...сохранение.


    При сохранении создается объект «Примечание», куда пишется описание из соответствующего поля и к нему прикладывается файл с фотографией и именем, состоящим из случайного числа. Примечание связано с Авансовым отчетом.

Patch(Примечания;Defaults(Примечания);{subject:Text(Now(); "[$-ru-RU]dd.mm.yyyy hh:mm");notetext:txtDescription.Text;_objectid_value:selectedExpense.'Авансовый отчет';_objectid_type:"awr_expensereports";filename:"img_"& Text(Rand())&".jpg";documentbody:If(StartsWith(First(Image).fileBody;"data:image");Replace(First(Image).fileBody;1; Len(Left(First(Image).fileBody;Find(",";First(Image).fileBody)));"");First(Image).fileBody)});;Reset(txtDescription);;Clear(Image)

    Признаюсь, что эту идею я нашел на просторах интернета, тут.
Зададим очистку получившейся фотографии, на случай, если фотография не удалась.


Список приложенных фотографий отобразим в галерее, заполненной список примечаний.


SortByColumns(Filter(Примечания; _objectid_value=selectedExpense.'Авансовый отчет');"createdon")

Удаление строки выглядит так


Remove(Примечания;galNotes_1.Selected)

Форма готова, теперь можно определить кнопку перехода на эту форму.


Navigate(scrnAddAttachment;None;{selectedExpense:galExpenseList.Selected})

Строки авансового отчета

Теперь нужно определить функционал добавления строк авансового отчета. Описание того, на что были потрачены деньги.
Создаем новый экран и добавляем туда форму добавления строк и галерею (список) для просмотра добавленных строк и их удаления.
Форма привязана к сущности строк авансового отчета. Не забудьте поменять режим формы по умолчанию на «Создать».


    Чтобы привязывать новый объект с родительским авансовым отчетом, нужно задать значение по умолчанию для поля «Авансовым отчет».


Так определяется кнопка сохранения



SubmitForm(frmEditExpenseRow);;ResetForm(frmEditExpenseRow)

Так сброс


Определим источник данных для списка строк.


SortByColumns(Filter('Строки авансового отчета'; 'Авансовый отчет'=selectedExpense.awr_expensereportid);"createdon")

Кнопка удаления строк


Кнопка выделения строки заполняет форму данными для редактирования.


Select(Parent);;EditForm(frmEditExpenseRow)

Кнопка возврата.


Определим кнопку перехода к строкам авансового отчета.


Navigate(scrnExpenseReportRows; ScreenTransition.None;{selectedExpense:selectedExpense})

Отправка в бухгалтерию

Когда отчет заполнен, нужно отправить его в бухгалтерию, нажатием на кнопку.

Patch('Авансовые отчеты';selectedExpense;{'Причина состояния':124000000});;Back()

Видимость кнопки зависит от статуса


Итог

Итогом всего процесса обычно является документ, в нашем случае его можно сформировать в CRM или в 1С после отработки процесса интеграции. Для работы рассылок из примечаний я создавал компонент на основе этого описания.
Хочется так же добавить, что я получил удовольствие от построения приложений на PowerApps, потому что этот инструмент позволяет получить функционал в максимально короткие сроки. Вы можете создавать приложения максимально быстро и при это не пускаете конечного пользователя напрямую в SharePoint или CRM, где сотруднику еще нужно разъяснить куда ему можно нажимать, а куда нет. К тому же приложение доступно на мобильных платформах. Кто сталкивался с тем как это долго и дорого создается, тот понимает в чем выгода.

















Комментариев нет:

Отправить комментарий