«Самый лучший гуй — одна большая кнопка
в центре экрана с надписью «Сделай мне хорошо!»
(с) Опытный дизайнер
«Самый лучший гуй — одна большая надпись
в центре экрана «Тебе уже хорошо!»
(с) Опытный программист
Цитата #437964
Неудобства: При работе с программой
приходится нажимать на различные кнопки
(с) bash.org.ru
В идеале каждая система должна стремиться к минимизации своего интерфейса.
В качестве плохого примера в системе SAP ERP можно вспомнить транзакцию проводки амортизации AFAB:
Первый экран выглядит очень типично для SAP ERP, поэтому и проблемы у этого экрана старые идеологические:
1. Первый экран (экран выбора) не даёт никакой информации о текущем состоянии
2. Все проверки происходят после заполнения всех параметров и запуска процесса
3. Объем задачи большой, заранее не просчитывается, поэтому установлено простое ограничение на тестовый прогон – 1000 записей
4. Формирование фонового задания и отслеживание его статуса – не самая простая пользовательская операция: пять кликов на запуск, пять кликов на просмотр, причём через непростые малознакомые экраны.
5. Нет никакой отчётливой сигнализации об ошибках расчёта
Я просто удивляюсь, почему за последние десять лет никто так и не решился переписать междумордие этой важной задачи.
BTW: Вполне вероятно, что в “Пульте управления закрытием” это и сделано, вот только это тема совсем другого разговора.
Приступим?
Предпосылки
Сначала обратим внимание на предпосылки, которые должны оказать влияние на интерфейс.
* * *
В этой транзакции пользователь обычно выполняет две рутинных операции:
- Один раз в месяц пользователь запускает расчет за последний месяц в режиме “по плану”.
- Иногда пользователь запускает расчет за предыдущий месяц в режиме “повторение”
* * *
Пользователь всегда прикреплён к одной определённой балансовой единице.
* * *
Амортизация выполняется по периодам строго последовательно и только вперёд. В системе контролируются статусы расчета по периодам.
* * *
Самый первый прогон всегда запускается вручную под пристальным наблюдением.
* * *
При прогоне амортизации не должны возникать ошибки. Если вдруг ошибки всё-таки возникают, то пользователь не может их решить самостоятельно.
* * *
Пользователю не нужно запускать тестовый прогон (режим моделирования без реальных проводок), всё сразу и взаправду.
* * *
Прогон амортизации выполняется не сразу, а занимает продолжительное время. Условный ориентир – пять минут.
* * *
В особо сложных случаях старая функциональность никуда не девается.
Интерфейс
При первом подходе с учетом требований выше нарисовался такой интерфейс:
Что показываем:
- Балансовую единицу не запрашиваем, просто показываем без вариантов.
- Текущий месяц определяется автоматически, без вариантов.
- Показываем дату последнего расчета.
Целых две кнопки:
- В предыдущий раз было не очень хорошо, давай ещё раз!
- Всё нормально, идём вперёд!
Можно ли сделать всего лишь одну кнопку? При большем количестве допущений – да. Например:
- Если последний месяц расчета = текущий, то можно выполнить только “Повтор”.
- Если последний месяц расчета < текущий, то можно выполнить только “По плану”(Вперёд). Если при этом можно проверить, что (а) последний месяц расчета всё ещё открыт в бухгалтерском учёте, (б) с момента последнего расчета есть влияющие изменения, тогда можно предложить предварительный запуск в режиме “Повтор”.
- При закрытии периода можно проверять наличие изменений, при которых желательно сделать прогон в режиме “Повтор”.
Это большие допущения, требующие более сложной реализации и обсуждения. Пока отложим.
Итак, если наш пользователь всё-таки нажимает одну их кнопок, то справа появляется журнал:
Журнал автоматически обновляется каждые пять секунд, чтобы пользователь мог проконтролировать результат, если ему хочется. Записи журнала лучше выводить в обратном порядке, таким образом самая последняя запись должна отражаться первой.
Если пользователь уверен в результате, то он может закрыть окно, не дожидаясь окончания. Фоновое задание продолжит работу в любом случае.
Реализация
А теперь рассмотрим на реализацию типичных задач, спрятанную под этим капотом.
Кроме обыкновенного ABAP-кода нам понадобятся следующие запчасти и велосипеды:
- Изменение надписей на кнопках в рантайме
- Формирование фонового задания
- Автообновление интерфейса
- Вывод результатов в виде журнала
Добавим мегакнопки
Обычно кнопки имеют неизменяемую надпись, но мы можем изменять надпись в рантайме.
Сначала нам понадобится пара переменных, по одной на кнопку:
data: bt_refresh type char40.
data: bt_next type char40.
Затем надо нарисовать кнопки на экране:
Остаётся только написать код, который склеит хитрое наименование для кнопки в нужный момент:
concatenate
gc_icon_refresh
‘Перерасчитать'(G01)
ls_t247-ltx
taba-afblgj
into bt_refresh
separated by space.
Здесь кроме обыкновенных переменных можно увидеть константу с кодом иконки:
constants: gc_icon_refresh type icon-id value ‘@42@’. "ICON_REFRESH
С иконками интерфейс выглядит красивее.
BTW: Кнопки могут быть и на панели инструментов, но это реализуется иначе.
Добавим фоновое задание
Если есть супертяжёлые вычисления, то мы можем выделить их в фоновое задание (JOB). В этом случае работа будет выполняться в отдельном потоке, не связанном с пользовательской сессией.
Один из самых простых способов сформировать задание: воспользоваться функциями JOB_OPEN и JOB_CLOSE. При этом подразумевается, что все вычисления вынесены в отдельную программу, которую можно вызвать используя конструкцию SUBMIT REPORT.
Шаг первый. Придумываем имя и открываем задание:
1 2 3 4 5 6 7 8 9 10 11 12 |
data: lv_jobname type tbtcjob-jobname. data: lv_jobcount type tbtcjob-jobcount. concatenate 'RAPOST2000' iv_bukrs into lv_jobname separated by '_'. call function 'JOB_OPEN' exporting jobname = lv_jobname importing jobcount = lv_jobcount exceptions others = 99. |
Шаг второй. Формулируем содержимое задания:
1 2 3 4 5 6 |
submit rapost2000 with p_bukrs = ... with p_gjahr = ... via job lv_jobname number lv_jobcount and return. |
Шаг третий. Закрываем задание:
1 2 3 4 5 6 7 |
call function 'JOB_CLOSE' exporting jobcount = lv_jobcount jobname = lv_jobname strtimmed = 'X' exceptions others = 99. |
После таких манипуляций без дополнительных параметров задание будет запущено немедленно.
Мы можем отследить дальнейшую судьбу этого задания по имени и идентификатору.
Добавим автообновление
Один из способов сделать автообновление экрана – реализация через таймер.
Сначала определяем таймер и его обработчик с событием:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
data: ob_timer type ref to cl_gui_timer. class lcl_event_handler definition. public section. class-methods: on_finished for event finished of cl_gui_timer. endclass. class lcl_event_handler implementation. method on_finished. ... "Если нам пора перерисовать экран, отправляем код экшена "Он не обязан существовать call method cl_gui_cfw=>set_new_ok_code exporting new_code = 'DUMMY'. ... "Если надо продолжать автообновление, то снова взводим таймер ob_timer->run( ). ... endmethod. endclass. data: event_handler type ref to lcl_event_handler. |
После этого осталось в нужный момент инициировать сам процесс автообновления:
1 2 3 4 5 |
create object ob_timer. create object event_handler . set handler event_handler->on_finished for ob_timer. ob_timer->interval = 5. ob_timer->run( ). |
Добавим журнал
Журнал можно реализовать при помощи HTML-элемента.
Сначала надо инициализировать элемент управления:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
data: gr_html_cont type ref to cl_gui_custom_container. data: gr_html type ref to cl_gui_html_viewer. if gr_html_cont is not bound. create object gr_html_cont exporting container_name = 'HTML_CONTROL'. endif. if gr_html is not bound. create object gr_html exporting parent = gr_html_cont. endif. |
А затем можно вставить внутрь нашего элемента любые HTML-фрагменты, например так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
data: lt_html type table of char100 with header line. data: lv_url type char255. append '<html><head><meta charset="utf-8"></head>' to lt_html. append ... to lt_html.... append '</html>' to lt_html. gr_html->load_data( exporting encoding = 'utf-8' charset = 'utf-8' importing assigned_url = lv_url changing data_table = lt_html[] ). gr_html->show_url( exporting url = lv_url ). |
И если все эти ингредиенты смешать в ABAP-редакторе, влить наполнителей и вкусовых добавок, то получится работоспособное решение.