Работаю ABAP разработчиком в одной из отечественных компаний, внедряющих SAP.
На днях от консультанта пришла спецификация с задачей сделать специальную строку подсуммирования (ALV GRID), в который будут всевозможные суммы, значения, наименования и т.д., которых по стандарту в ней быть не должно. Также нужно реализовать возможность пользователю изменять данные в этой строке вручную.
Для тех, кто в танке: строки подсуммирования — желтенькие, в них выводятся подсуммы, в данном случае по дебитору.
Пораскинув мозгами и поспрашивав коллег,решил послать консультанта куда подальше с такими запросами получил 2 варианта решения:
Первый вариант сразу выкинул. Буквально недавно правил разработку с такой строкой, геморроя не наберешься — нельзя сортировать фильтровать данные в ALV, строка может куда-то съехать и т.д. и т.п. К тому же в стандартной строке есть куча классных и полезных плюшечек типа схлопывания строк в одну или иерархия подсумм. Единственный минус — как ни крутись, открыть стандартную строку подсуммирования на редактирование — невозможно (ну или чересчур трудозатратно).
Второй вариант — а так вообще можно?! Получив от начальства одобрение на исследование вопроса, полез гуглить, и на первой же ссылке добрый индус объясняет, что это вполне законно и реально. В классе CL_GUI_ALV_GRID есть метод GET_SUBTOTALS, возвращающий ссылки на таблицы с суммами всех уровней, которые можно спокойно изменять в своей программе. Выглядит он так:
Ура, товарищи! Задача понятна, и даже наметился план действий. Левые значения нужно вставлять только в строки подсуммировавния для дебиторов. Т.е. передаем в метод SET_TABLE_FOR_FIRST_DISPLAY параметр IT_SORT, в котором указываем первый уровень сортировки — сортировку по дебитору. Вытаскиваем таблицу сумм 1-ого уровня (это будут суммы для дебиторов) и подставляем туда нужные значения:
Схематически выглядит так:
«Неужели это сработает?!», подумал я, и трясущимися руками запустил отчет. Сработало!!! Но не совсем верно. Значения вывелись только в сабтоталы, оказавшиеся «под» экраном. Т.е. те, к которым нужно скроллить вниз. Решение нашлось довольно быстро на просторах интернета: вызвать мягкое обновление ALV после изменения подсумм:
Если не указывать параметр 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 — строка подсуммировки (есть еще значение 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.
Ну вот вроде и все! Все довольны, все работает! Ну по крайней мере у меня, пока консультант в отпуске и еще не проверял.
На днях от консультанта пришла спецификация с задачей сделать специальную строку подсуммирования (ALV GRID), в который будут всевозможные суммы, значения, наименования и т.д., которых по стандарту в ней быть не должно. Также нужно реализовать возможность пользователю изменять данные в этой строке вручную.
Для тех, кто в танке: строки подсуммирования — желтенькие, в них выводятся подсуммы, в данном случае по дебитору.
Пораскинув мозгами и поспрашивав коллег,
- Сделать свою строку подсуммирования, которая будет лежать во внутренней табличке. Настроить для нее вывод с цветом, суммами и т.д.
- Попробовать подставить необходимые значения в стандартную строку подсуммирования.
Первый вариант сразу выкинул. Буквально недавно правил разработку с такой строкой, геморроя не наберешься — нельзя сортировать фильтровать данные в ALV, строка может куда-то съехать и т.д. и т.п. К тому же в стандартной строке есть куча классных и полезных плюшечек типа схлопывания строк в одну или иерархия подсумм. Единственный минус — как ни крутись, открыть стандартную строку подсуммирования на редактирование — невозможно (ну или чересчур трудозатратно).
Второй вариант — а так вообще можно?! Получив от начальства одобрение на исследование вопроса, полез гуглить, и на первой же ссылке добрый индус объясняет, что это вполне законно и реально. В классе CL_GUI_ALV_GRID есть метод GET_SUBTOTALS, возвращающий ссылки на таблицы с суммами всех уровней, которые можно спокойно изменять в своей программе. Выглядит он так:
Ура, товарищи! Задача понятна, и даже наметился план действий. Левые значения нужно вставлять только в строки подсуммировавния для дебиторов. Т.е. передаем в метод 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.
Ну вот вроде и все! Все довольны, все работает! Ну по крайней мере у меня, пока консультант в отпуске и еще не проверял.
KaaPex
Довольно тривиальная задача, статья для новичков будет полезна. Предлагаю решить задачу, когда есть множественный выбор строк, пользователь выбирает произвольное строки и хочет их сгруппировать, по средствам нажатия кнопки или из контекстного меню, при этом весь ваш функционал подсуммовки и редактирования числовых ячеек сохраняется.
Ben_armyman
Добрый день! Если я правильно понял задачу, то можно попробовать добавить техническое поле — критерий сортировки. Изначально во всех строках он пустой, затем пользователь выбирает произвольные строки, нажимает кнопку «сгруппировать», и для этих строк заполняем новое поле одинаковым значением (допустим единичкой). Далее добавляем наше поле в сортировки АЛВ, обновляем АЛВ что бы он посчитал суммы и меняем сабтоталы как нам нужно. Такой вариант подойдет?
KaaPex
Вполне, но не забывайте, что возможен случай вложенной группировки.