Pull to refresh

SAP — ABAP. Изменение строки подсуммирования в ALV grid

Reading time5 min
Views16K
Работаю ABAP разработчиком в одной из отечественных компаний, внедряющих SAP.

На днях от консультанта пришла спецификация с задачей сделать специальную строку подсуммирования (ALV GRID), в который будут всевозможные суммы, значения, наименования и т.д., которых по стандарту в ней быть не должно. Также нужно реализовать возможность пользователю изменять данные в этой строке вручную.

Для тех, кто в танке: строки подсуммирования — желтенькие, в них выводятся подсуммы, в данном случае по дебитору.

image

Пораскинув мозгами и поспрашивав коллег, решил послать консультанта куда подальше с такими запросами получил 2 варианта решения:

  1. Сделать свою строку подсуммирования, которая будет лежать во внутренней табличке. Настроить для нее вывод с цветом, суммами и т.д.
  2. Попробовать подставить необходимые значения в стандартную строку подсуммирования.

Первый вариант сразу выкинул. Буквально недавно правил разработку с такой строкой, геморроя не наберешься — нельзя сортировать фильтровать данные в ALV, строка может куда-то съехать и т.д. и т.п. К тому же в стандартной строке есть куча классных и полезных плюшечек типа схлопывания строк в одну или иерархия подсумм. Единственный минус — как ни крутись, открыть стандартную строку подсуммирования на редактирование — невозможно (ну или чересчур трудозатратно).

Второй вариант — а так вообще можно?! Получив от начальства одобрение на исследование вопроса, полез гуглить, и на первой же ссылке добрый индус объясняет, что это вполне законно и реально. В классе CL_GUI_ALV_GRID есть метод GET_SUBTOTALS, возвращающий ссылки на таблицы с суммами всех уровней, которые можно спокойно изменять в своей программе. Выглядит он так:

image

Ура, товарищи! Задача понятна, и даже наметился план действий. Левые значения нужно вставлять только в строки подсуммировавния для дебиторов. Т.е. передаем в метод SET_TABLE_FOR_FIRST_DISPLAY параметр IT_SORT, в котором указываем первый уровень сортировки — сортировку по дебитору. Вытаскиваем таблицу сумм 1-ого уровня (это будут суммы для дебиторов) и подставляем туда нужные значения:

Схематически выглядит так:

FORM fill_sort  CHANGING ct_sort TYPE lvc_t_sort.

  APPEND INITIAL LINE TO ct_sort ASSIGNING FIELD-SYMBOL(<lfs_sort>).
  <lfs_sort>-spos      = '1'.
  <lfs_sort>-fieldname = 'KUNNR'. "Фууууууу хардкод!!!!
  <lfs_sort>-subtot    = 'X'.
  <lfs_sort>-up        = 'X'.

ENDFORM.

go_alv_grid->set_table_for_first_display( ... ).
PERFORM set_subtotals.
...
FORM set_subtotals .
  DATA: lr_collect01   TYPE REF TO data
      .
  FIELD-SYMBOLS: <lfs_tab>  TYPE ANY TABLE
               , <lfs_line> TYPE ty_data
               .
  go_alv_grid->get_subtotals(
    IMPORTING
      ep_collect01   = lr_collect01
    ).

  ASSIGN lr_collect01->* TO <lfs_tab>.
  IF <lfs_tab> IS ASSIGNED.
    LOOP AT <lfs_tab> ASSIGNING <lfs_line>.
       "Подставляем необходимые значения в строку
    ENDLOOP.
  ENDIF.
ENDFORM.

«Неужели это сработает?!», подумал я, и трясущимися руками запустил отчет. Сработало!!! Но не совсем верно. Значения вывелись только в сабтоталы, оказавшиеся «под» экраном. Т.е. те, к которым нужно скроллить вниз. Решение нашлось довольно быстро на просторах интернета: вызвать мягкое обновление ALV после изменения подсумм:

go_alv_grid->refresh_table_display( i_soft_refresh = 'X' ).

Если не указывать параметр i_soft_refresh, то обновление пересчитает все сабтоталы и затрет наши данные.

Все?! Можно отдавать на тест?! Ну, это было не так уж и сл… так, стойте… а если пользователь изменит сортировку? Или отфильтрует данные? Или вообще сделает что угодно с нашей таблицей!? Она же обновится и все наши значения исчезнут! Черт…

Но, как оказалось, в классе CL_GUI_ALV_GRID есть волшебное и очень полезное событие: AFTER_REFRESH, которое запускается каждый раз, когда ALV обновляется. На этот раз план действия таков:

1. Ловим событие AFTER_REFRESH;
2. Получаем критерии сортировки текущего состояния ALV (которое сейчас выведется на экран): go_alv_grid->get_sort_criteria;
3. Проверяем: на первом уровне сортировки должен быть дебитор (<lfs_sort>-spos = 1 AND <lfs_sort>-fieldname = 'KUNNR') и должна быть включена подсуммировка (<lfs_sort>-subtot = 'X');
3.а. Если все ок, запускаем подпрограмму, которая подставляет нужные данные в строки подсуммирования: PERFORM set_subtotals.;
3.б. Иначе изменяем критерии сортировки таким образом, что бы на первом уровне стоял дебитор и для него была включена подсуммировка;
3.б.I. Если дебитора вообще нет в критерия сортировки, то удляем сортировку 9-ого уровня (если она есть), увеличиваем все остальные уровни на +1, и вставляем первым уровнем сортировку по дебитору;
3.б.II. Если сортировка по дебитору есть в таблице, но не на первом уровне, то увеличиваем все уровни сортировок меньшие уровня дебитора на +1, и меняем уровень сортировки дебитора на 1;

4. Устанавливаем верные критерии сортировки: go_alv_grid->set_sort_criteria( lt_sort );
5. Далее запускаем обновление ALV с пересчетом сумм (здесь надо быть аккуратнее, что бы не запустить бесконечную рекурсию, т.к. событие AFTER_REFRESH отработает еще раз);
6. Событие отработает 2-ой раз, но критерии сортировки будут верными, и запустится заполнение строк сабтотала (кстати тут проблема — необходимо сделать проверку, на случай если установлены верные суммы, что бы программа не зависла из-за бесконечной рекурсии, т.к. после изменения сумм нам нужно запустить мягкое обновление ALV).

Профит! Получаем похвалу от начальства и консультанта! Как бы пользователь не мучил ALV, на первом уровне сортировки всегда будет дебитор, для него всегда будет строка подсуммировки с нужными значениями.

Осталась только одна проблема — изменять данные вручную пользователь до сих пор не может. А хочет. Но, пообщавшись с консультантом, решили отлавливать даблклик по такой строке и выкидывать окошко с полями ввода, которые заполнит пользователь, нажмет Enter и они попадут в необходимые поля в таблице. Возникает вопрос, как строку подсуммировки отличить от обычной? Ответ: да легко конечно! Что вообще за глупые вопросы? В событии DOUBLE_CLICK есть отличный параметр: is_row-rowtype. Он пустой в обычных строках, и заполнен в служебных. Заполнен он обычно строкой такого типа:

S 0101 0000000001

Экспериментальным путем выяснилось, что S — строка подсуммировки (есть еще значение T — итоговая строка сумм), 0101 — уровень сортировки (по крайней мере последние 2 цифры). Второе поле структуры is_row (index) содержит номер строки таблицы сумм необходимого уровня. Вся информация есть! Действуем:

1. Проверяем, что пользователь кликнул по сабтоталу;
2. Получаем таблицу сумм первого уровня: go_alv_grid->get_subtotals;
3. Читаем необходимую строку sub_total_tab[ is_row-index ];
4. Показываем пользователю окно с полями ввода (вставляем в них текущие значения строки);
5. Получаем пользовательские данные;
6. Вставляем пользовательские данные в нашу строку;
7. Мягко обновляем ALV.

Ну вот вроде и все! Все довольны, все работает! Ну по крайней мере у меня, пока консультант в отпуске и еще не проверял.
Tags:
Hubs:
+7
Comments4

Articles

Change theme settings