Оказывается "партии" (charg) и "Виды оценки" (bwtar) - совсем не одно и то же, как меня когда-то убедил кто-то из консультантов.
На сбытовых заводах у нас они соответствуют 1к1, а, например, на балансовых - не соответствуют.
Соответствие можно восстановить в таблице mcha. Там по материалу/заводу/"партии" можно узнать вид оценки.
Подробнее...
Сюда я буду сохранять всякие куски кода, конфигов, ссылки, чтобы в один прекрасный день когда мне это снова понадобится - не вспоминать судорожно где и когда я это применял.
13 июля 2011 г.
27 июня 2011 г.
Работа с блокировками
1. Найти Функции блокировки/разблокировки
Например, необходимо блокировать/разблокировать элемент таблицы vbak (сбытовой заказ).
* В транзакции se11 в графе "ОбъектБлокировки" ищем "*vbak*" и находим "EVVBAKE".
* Заходи внутрь и нажимаем "Перейти к" - "Модули блокировки"
* Там видим:
DEQUEUE_EVVBAKE Снятие блок. постановки в очередь с объекта EVVBAKE
ENQUEUE_EVVBAKE Запрос блокировки постановки в очередь для объекта EVVBAKE
есть ещё две, но по ним видно, что это что-то специфическое, потому что там блокировка идёт не по vbak-vbeln, а по совершенно другому полю.
2. Снять все блокировки
Эти функции работают не как "блокировать" / "разблокировать", а "увеличить счётчик блокировок" / "уменьшить счетчик блокировок". В некоторых случаях это не очень удобно, например когда мы не можем или не хотим следить за количеством блокировок (в моём случае стандартные ФМ-ы непредсказуемо снимали или устанавливали блокировки).
Вот такой код вернёт в E_COUNT количество блокировок текущим пользователем по заказу l_vbeln:
data lt_enq type table of seqg3.
data ls_enq type seqg3.
data: GTARG TYPE EQEGTARG.
CONCATENATE SY-MANDT l_vbeln into GTARG.
CALL FUNCTION 'ENQUEUE_REPORT'
EXPORTING
GCLIENT = SY-MANDT
GNAME = 'VBAK'
GTARG = GTARG
GUNAME = SY-UNAME
TABLES
ENQ = lt_enq
EXCEPTIONS
COMMUNICATION_FAILURE = 1
SYSTEM_FAILURE = 2
OTHERS = 3.
E_COUNT = 0.
LOOP AT lt_enq into ls_enq.
E_COUNT = E_COUNT + ls_enq-GUSEVB.
ENDLOOP.
Соответственно для полной разблокировки надо запускать соответсвующий фм столько раз.
А при блокировании лучше проверить, а не заблокирован ли уже заказ.
Так же могут быть полезны другие модули группы функций SENT.
3. Ожидание разблокировки стандартными FM-ами
Функции блокировки могут пригодиться в случае, когда по одному объекту (например, сбытовому заказу) необходимо последовательно выполнять разные BAPI-функции. Дело в том, что эти функции часто работаю асинхронно, и возвращают управление в вызывающую функцию раньше, чем закончат свою работу. Не помогает даже bapi_transaction_commit с признаком wait. То есть второй шаг обработки будет провален так как, первый шаг ещё не отработал.
В таких случаях может быть полезен следующий код:
* вызов первой функции, блокирующей заказ. например, SD_SALESDOCUMENT_CREATE.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING WAIT = 'X'.
is_ok = space.
DO 10 TIMES. " количество попыток, сколько раз пытаться блокировать заказ. не слишком много
* вызов фм блокирования
IF SY-SUBRC = 0. " если удалось заблокировать
* вызов функции разблокирования
is_ok = 'X'.
exit.
else.
* если надо - запоминаем почему неудалось заблокировать. это хранится в sy-MSG*.
ENDIF.
WAIT UP TO 1 SECONDS. " делаем паузу перед следующей попыткой заблокировать
ENDDO.
if is_ok is not initial.
* вызов второй функции, блокирующей заказ.
endif.
Подробнее...
Например, необходимо блокировать/разблокировать элемент таблицы vbak (сбытовой заказ).
* В транзакции se11 в графе "ОбъектБлокировки" ищем "*vbak*" и находим "EVVBAKE".
* Заходи внутрь и нажимаем "Перейти к" - "Модули блокировки"
* Там видим:
DEQUEUE_EVVBAKE Снятие блок. постановки в очередь с объекта EVVBAKE
ENQUEUE_EVVBAKE Запрос блокировки постановки в очередь для объекта EVVBAKE
есть ещё две, но по ним видно, что это что-то специфическое, потому что там блокировка идёт не по vbak-vbeln, а по совершенно другому полю.
2. Снять все блокировки
Эти функции работают не как "блокировать" / "разблокировать", а "увеличить счётчик блокировок" / "уменьшить счетчик блокировок". В некоторых случаях это не очень удобно, например когда мы не можем или не хотим следить за количеством блокировок (в моём случае стандартные ФМ-ы непредсказуемо снимали или устанавливали блокировки).
Вот такой код вернёт в E_COUNT количество блокировок текущим пользователем по заказу l_vbeln:
data lt_enq type table of seqg3.
data ls_enq type seqg3.
data: GTARG TYPE EQEGTARG.
CONCATENATE SY-MANDT l_vbeln into GTARG.
CALL FUNCTION 'ENQUEUE_REPORT'
EXPORTING
GCLIENT = SY-MANDT
GNAME = 'VBAK'
GTARG = GTARG
GUNAME = SY-UNAME
TABLES
ENQ = lt_enq
EXCEPTIONS
COMMUNICATION_FAILURE = 1
SYSTEM_FAILURE = 2
OTHERS = 3.
E_COUNT = 0.
LOOP AT lt_enq into ls_enq.
E_COUNT = E_COUNT + ls_enq-GUSEVB.
ENDLOOP.
Соответственно для полной разблокировки надо запускать соответсвующий фм столько раз.
А при блокировании лучше проверить, а не заблокирован ли уже заказ.
Так же могут быть полезны другие модули группы функций SENT.
3. Ожидание разблокировки стандартными FM-ами
Функции блокировки могут пригодиться в случае, когда по одному объекту (например, сбытовому заказу) необходимо последовательно выполнять разные BAPI-функции. Дело в том, что эти функции часто работаю асинхронно, и возвращают управление в вызывающую функцию раньше, чем закончат свою работу. Не помогает даже bapi_transaction_commit с признаком wait. То есть второй шаг обработки будет провален так как, первый шаг ещё не отработал.
В таких случаях может быть полезен следующий код:
* вызов первой функции, блокирующей заказ. например, SD_SALESDOCUMENT_CREATE.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING WAIT = 'X'.
is_ok = space.
DO 10 TIMES. " количество попыток, сколько раз пытаться блокировать заказ. не слишком много
* вызов фм блокирования
IF SY-SUBRC = 0. " если удалось заблокировать
* вызов функции разблокирования
is_ok = 'X'.
exit.
else.
* если надо - запоминаем почему неудалось заблокировать. это хранится в sy-MSG*.
ENDIF.
WAIT UP TO 1 SECONDS. " делаем паузу перед следующей попыткой заблокировать
ENDDO.
if is_ok is not initial.
* вызов второй функции, блокирующей заказ.
endif.
Подробнее...
26 июня 2011 г.
FM для вывода диалога с несколькими опциями
DATA: lt_spopli TYPE TABLE OF SPOPLI,
ls_spopli TYPE SPOPLI.
DATA: lv_answer TYPE CHAR2.
CLEAR ls_spopli.
ls_spopli-VAROPTION = 'Предложение'.
APPEND ls_spopli TO lt_spopli.
CLEAR ls_spopli.
ls_spopli-VAROPTION = 'Заказ с оплатой на месте'.
APPEND ls_spopli TO lt_spopli.
CALL FUNCTION 'POPUP_TO_DECIDE_LIST'
EXPORTING
CURSORLINE = 1
TEXTLINE1 = 'Сохранить документ как:'
TITEL = 'Cохранить'
IMPORTING
ANSWER = lv_answer
TABLES
T_SPOPLI = lt_spopli
EXCEPTIONS
NOT_ENOUGH_ANSWERS = 1
TOO_MUCH_ANSWERS = 2
TOO_MUCH_MARKS = 3
OTHERS = 4
.
IF SY-SUBRC <> 0.
MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
Результат:
Подробнее...
ls_spopli TYPE SPOPLI.
DATA: lv_answer TYPE CHAR2.
CLEAR ls_spopli.
ls_spopli-VAROPTION = 'Предложение'.
APPEND ls_spopli TO lt_spopli.
CLEAR ls_spopli.
ls_spopli-VAROPTION = 'Заказ с оплатой на месте'.
APPEND ls_spopli TO lt_spopli.
CALL FUNCTION 'POPUP_TO_DECIDE_LIST'
EXPORTING
CURSORLINE = 1
TEXTLINE1 = 'Сохранить документ как:'
TITEL = 'Cохранить'
IMPORTING
ANSWER = lv_answer
TABLES
T_SPOPLI = lt_spopli
EXCEPTIONS
NOT_ENOUGH_ANSWERS = 1
TOO_MUCH_ANSWERS = 2
TOO_MUCH_MARKS = 3
OTHERS = 4
.
IF SY-SUBRC <> 0.
MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
Результат:
Подробнее...
30 мая 2011 г.
Несколько полезных записей из внешнего интернета
Как найти что-то в исходных кодах
Для поиска чего либо в исходных кодах, очень помогает программа
RPR_ABAP_SOURCE_SCAN
Как запустить отладку в модальном окне
Вот так можно включить отладку в модальном окне:
Создаем текстовый файл со следующим содержанием, например, на рабочем столе
[FUNCTION]
Command=/H
Title=Debugger
Type=SystemCommand
Теперь просто перетаскиваем этот файл на ваше окошко и все.
Как редактировать данные в таблицах без диалога ведения, или в закрытых мандантах
Для того, что бы редактировать данные в таблицах без диалога ведения, или в закрытых мандантах необходимо:
В транзакции UASE16N выбрать таблицу, данные которой необходимо редактировать, ввести команду &SAP_EDIT в командную строку и нажать «Выполнить». Подробнее...
Для поиска чего либо в исходных кодах, очень помогает программа
RPR_ABAP_SOURCE_SCAN
Как запустить отладку в модальном окне
Вот так можно включить отладку в модальном окне:
Создаем текстовый файл со следующим содержанием, например, на рабочем столе
[FUNCTION]
Command=/H
Title=Debugger
Type=SystemCommand
Теперь просто перетаскиваем этот файл на ваше окошко и все.
Как редактировать данные в таблицах без диалога ведения, или в закрытых мандантах
Для того, что бы редактировать данные в таблицах без диалога ведения, или в закрытых мандантах необходимо:
В транзакции UASE16N выбрать таблицу, данные которой необходимо редактировать, ввести команду &SAP_EDIT в командную строку и нажать «Выполнить». Подробнее...
Вывести результат стандартных функций (BAPIRET2)

Красиво показать результат вызова стандартного ФМ:
CALL FUNCTION 'C14ALD_BAPIRET2_SHOW'
TABLES
I_BAPIRET2_TAB = RETURN.
Неожиданно приятная функция, не иначе как брахман писал. Подробнее...
20 мая 2011 г.
Быстрое создание ALV-таблицы
" например надо отобразить таблицу T_TCJ_POSITIONS
DATA: gc_alv_table TYPE REF TO cl_salv_table.
cl_salv_table=>factory( IMPORTING r_salv_table = gc_alv_table CHANGING t_table = T_TCJ_POSITIONS ).
" показать
gc_alv_table->display( ).
" если необходимо добавить события:
data: events_tb TYPE REF TO CL_SALV_EVENTS_TABLE.
CALL METHOD gc_alv_table->GET_EVENT
RECEIVING
VALUE = events_tb.
data ALV_HANDLER_pay type ref to CL_ALV_HANDLER_pay.
create object ALV_HANDLER_pay.
set handler ALV_HANDLER_pay->handle_doubleclick for events_tb.
" Переименовать колонки:
gc_columns = gc_alv_table->get_columns( ).
gc_colwork ?= gc_columns->get_column( 'P_RECEIPTS' ).
gc_colwork->set_long_text( 'Оплата' ).
gc_colwork->set_medium_text( 'Оплата' ).
gc_colwork->set_short_text( 'Оплата' ).
" там же можно сделать прочие настройки, если надо
" добавим агрегацию
gc_aggr = gc_alv_table->get_aggregations( ).
gc_aggr->add_aggregation( 'P_RECEIPTS' ). Подробнее...
DATA: gc_alv_table TYPE REF TO cl_salv_table.
cl_salv_table=>factory( IMPORTING r_salv_table = gc_alv_table CHANGING t_table = T_TCJ_POSITIONS ).
" показать
gc_alv_table->display( ).
" если необходимо добавить события:
data: events_tb TYPE REF TO CL_SALV_EVENTS_TABLE.
CALL METHOD gc_alv_table->GET_EVENT
RECEIVING
VALUE = events_tb.
data ALV_HANDLER_pay type ref to CL_ALV_HANDLER_pay.
create object ALV_HANDLER_pay.
set handler ALV_HANDLER_pay->handle_doubleclick for events_tb.
" Переименовать колонки:
gc_columns = gc_alv_table->get_columns( ).
gc_colwork ?= gc_columns->get_column( 'P_RECEIPTS' ).
gc_colwork->set_long_text( 'Оплата' ).
gc_colwork->set_medium_text( 'Оплата' ).
gc_colwork->set_short_text( 'Оплата' ).
" там же можно сделать прочие настройки, если надо
" добавим агрегацию
gc_aggr = gc_alv_table->get_aggregations( ).
gc_aggr->add_aggregation( 'P_RECEIPTS' ). Подробнее...
генерированные экраны в своих формах
Чтобы отобразить генерированные экраны в своих формах надо описать экран:
SELECTION-SCREEN BEGIN OF SCREEN 0101 AS SUBSCREEN.
SELECT-OPTIONS s_vkorg FOR vbak-vkorg.
SELECT-OPTIONS s_vkbur FOR vbak-vkbur.
SELECT-OPTIONS s_VKGRP FOR vbak-VKGRP.
SELECT-OPTIONS s_vtweg FOR vbak-vtweg.
SELECT-OPTIONS s_spart FOR vbak-spart.
SELECT-OPTIONS s_SUBMI FOR vbak-SUBMI.
SELECT-OPTIONS s_netwr FOR vbap-netwr.
SELECT-OPTIONS s_lifnr FOR vbpa-lifnr.
SELECTION-SCREEN END OF SCREEN 0101.
А в своём экране включить его в подэкран аналогично обычным экранам. Подробнее...
SELECTION-SCREEN BEGIN OF SCREEN 0101 AS SUBSCREEN.
SELECT-OPTIONS s_vkorg FOR vbak-vkorg.
SELECT-OPTIONS s_vkbur FOR vbak-vkbur.
SELECT-OPTIONS s_VKGRP FOR vbak-VKGRP.
SELECT-OPTIONS s_vtweg FOR vbak-vtweg.
SELECT-OPTIONS s_spart FOR vbak-spart.
SELECT-OPTIONS s_SUBMI FOR vbak-SUBMI.
SELECT-OPTIONS s_netwr FOR vbap-netwr.
SELECT-OPTIONS s_lifnr FOR vbpa-lifnr.
SELECTION-SCREEN END OF SCREEN 0101.
А в своём экране включить его в подэкран аналогично обычным экранам. Подробнее...
3 мая 2011 г.
Оптимизация запросов select
Ряд рекомендаций:
1. Отказаться от into corresponding fields of table в пользу into table. Причём выводить только нужные поля.
Очень хорошо помогает.
Однако для этого надо обеспечить, что порядок и тип полей в таблице-получателе совадал с порядком полей запроса.
2. Не смешивать в запросе буферизованные таблицы и не буферизованные: буферизация работать не будет. Лучше вынести получение в отдельные подзапросы - можно даже в цикле, работает быстро.
На примере заполнения имён заводов и складов:
select [...] from [...] into table RES_TB where [...].
FIELD-SYMBOLS <res_fs> TYPE res_tp.
LOOP AT RES_TB ASSIGNING <res_fs>.
SELECT SINGLE name1 into <res_fs>-werks_t from t001w where werks = <res_fs>-werks.
SELECT SINGLE LGOBE into <res_fs>-LGORT_t from t001l where werks = <res_fs>-werks and lgort = <res_fs>-lgort.
endloop.
3. Например, в конструкции where есть ограничения "fielndname <> 'X'". А список допустимых значений этого поля небольшой и известен или может получен взят из справочника. Лучше постараться заменить на "=" в каком-либо виден (in, for all entries). либо вообще получить избыточные данные и отбросить их программным кодом (delete [...] where "fielndname = 'X'").
4. Например, результат первого запроса (tab1) используется во втором запросе (tab2), а потом эти данные собираются в общую таблицу. Примерно так:
loop at tab1 assigning <tab1_fs>.
read table tab2 into ls_tab2 with key field1 = <tab1_fs>-fielnd1 [...].
<tab1_fs>-field5 = ls_tab2-field5.
" либо цикл по второй таблице с аналогичным фильтром
endloop.
На больших объёмах такая сборка может быть очень медленной.
В таких случаях бывает полезно объявить таблицу tab2 как hash, если это возможно.
5. В фильтрах и соединениях таблиц чтобы использовался индекс надо перечислить поля, стоящие выше в индексе. Даже если там константа.
Например, в таблице A512 ключ из полей: MANDT, KAPPL, KSCHL, VKORG, BWTAR, MATNR, KFRST, DATBI. А в фильтре используется только vkorg, BWTAR, matnr, а KAPPL, KSCHL - константы. Необходимо перечислить эти два поля. MANDT - необязательно, его подставит система.
см также по FOR ALL ENTRIES
to be continue... Подробнее...
1. Отказаться от into corresponding fields of table в пользу into table. Причём выводить только нужные поля.
Очень хорошо помогает.
Однако для этого надо обеспечить, что порядок и тип полей в таблице-получателе совадал с порядком полей запроса.
2. Не смешивать в запросе буферизованные таблицы и не буферизованные: буферизация работать не будет. Лучше вынести получение в отдельные подзапросы - можно даже в цикле, работает быстро.
На примере заполнения имён заводов и складов:
select [...] from [...] into table RES_TB where [...].
FIELD-SYMBOLS <res_fs> TYPE res_tp.
LOOP AT RES_TB ASSIGNING <res_fs>.
SELECT SINGLE name1 into <res_fs>-werks_t from t001w where werks = <res_fs>-werks.
SELECT SINGLE LGOBE into <res_fs>-LGORT_t from t001l where werks = <res_fs>-werks and lgort = <res_fs>-lgort.
endloop.
3. Например, в конструкции where есть ограничения "fielndname <> 'X'". А список допустимых значений этого поля небольшой и известен или может получен взят из справочника. Лучше постараться заменить на "=" в каком-либо виден (in, for all entries). либо вообще получить избыточные данные и отбросить их программным кодом (delete [...] where "fielndname = 'X'").
4. Например, результат первого запроса (tab1) используется во втором запросе (tab2), а потом эти данные собираются в общую таблицу. Примерно так:
loop at tab1 assigning <tab1_fs>.
read table tab2 into ls_tab2 with key field1 = <tab1_fs>-fielnd1 [...].
<tab1_fs>-field5 = ls_tab2-field5.
" либо цикл по второй таблице с аналогичным фильтром
endloop.
На больших объёмах такая сборка может быть очень медленной.
В таких случаях бывает полезно объявить таблицу tab2 как hash, если это возможно.
5. В фильтрах и соединениях таблиц чтобы использовался индекс надо перечислить поля, стоящие выше в индексе. Даже если там константа.
Например, в таблице A512 ключ из полей: MANDT, KAPPL, KSCHL, VKORG, BWTAR, MATNR, KFRST, DATBI. А в фильтре используется только vkorg, BWTAR, matnr, а KAPPL, KSCHL - константы. Необходимо перечислить эти два поля. MANDT - необязательно, его подставит система.
см также по FOR ALL ENTRIES
to be continue... Подробнее...
Подводные камни FOR ALL ENTRIES
1. Если результаты одного запроса используются в конструкции for all entries второго - не забываем проверять, что таблица пустая. Иначе второй запрос не посчитает это ограничением.
select [...] into table table1 from [...] where [...].
if table1 is not initial.
select [...] into table table2 from [...] for all entries table1 where [...].
endif.
2. При For all entries могут не выводиться дублирующиеся строки (как будто используется параметр distinct).
Необходимо перечислить в списке полей вывода все ключевые поля всех используемых в запрос таблиц (причём даже если значение каких-то из них фактически одинаковые).
Проблема похоже в том, что такой запрос преобразуется системой в набор запросов, объединённых через метрику UNION, а не UNION ALL.
3. Например в конструкции where запрос используется набор полей из таблицы конструкции for all entries, а значения эти неуникальные в таблице (например только номера документов, а в таблице встречаются и позиции).
Такой запрос можно ускорить:
data tab_copy like tab. " либо type с тем же типом
tab_copy = tab.
sort tab_copy by vbeln." список полей, используемых в for all entries
DELETE ADJACENT DUPLICATES FROM tab_copy COMPARING vbeln. " тот же список полей
И в запросе использовать эту таблицу.
4. Иногда полезно убрать лишние фильтр по значениям из таблицы for all entries (например, вместо vbeln+posnr[+etern] оставить только vbeln) и применить пункт 3.
Особенно когда известно, что скорее всего в таблице источнике перечислены все или многие значения исключаемых полей.
Пусть даже будет какая-то избыточность данных, но запрос отработает значительно быстрее. Подробнее...
select [...] into table table1 from [...] where [...].
if table1 is not initial.
select [...] into table table2 from [...] for all entries table1 where [...].
endif.
2. При For all entries могут не выводиться дублирующиеся строки (как будто используется параметр distinct).
Необходимо перечислить в списке полей вывода все ключевые поля всех используемых в запрос таблиц (причём даже если значение каких-то из них фактически одинаковые).
Проблема похоже в том, что такой запрос преобразуется системой в набор запросов, объединённых через метрику UNION, а не UNION ALL.
3. Например в конструкции where запрос используется набор полей из таблицы конструкции for all entries, а значения эти неуникальные в таблице (например только номера документов, а в таблице встречаются и позиции).
Такой запрос можно ускорить:
data tab_copy like tab. " либо type с тем же типом
tab_copy = tab.
sort tab_copy by vbeln." список полей, используемых в for all entries
DELETE ADJACENT DUPLICATES FROM tab_copy COMPARING vbeln. " тот же список полей
И в запросе использовать эту таблицу.
4. Иногда полезно убрать лишние фильтр по значениям из таблицы for all entries (например, вместо vbeln+posnr[+etern] оставить только vbeln) и применить пункт 3.
Особенно когда известно, что скорее всего в таблице источнике перечислены все или многие значения исключаемых полей.
Пусть даже будет какая-то избыточность данных, но запрос отработает значительно быстрее. Подробнее...
ALV-контейнеры с одинаковыми именами
Проблема: В программе с ALV-контейнером на экране вызывается фм. В рамках ФМ-а открываются другие экраны с ALV-контейнерами. Один из которых называется также, как контейнер программы.
В результате при возврате из фм в программу ALV может не отрисовываться.
Выход: делать ALV-контейнеры с уникальными именами. Работает.
Возможно можно избежать такой проблемы подобрав параметры конструктора контейнера (указать программу-REPID и экран-DYNNR), но у меня не получилось. Подробнее...
В результате при возврате из фм в программу ALV может не отрисовываться.
Выход: делать ALV-контейнеры с уникальными именами. Работает.
Возможно можно избежать такой проблемы подобрав параметры конструктора контейнера (указать программу-REPID и экран-DYNNR), но у меня не получилось. Подробнее...
Подписаться на:
Сообщения (Atom)