14 апреля 2016 г.

Редактирование дебиторов/кредиторов

Дебиторы: 
  • пакет - CMD_INTERFACE (Customer Interface), 
  • основной класс - CMD_EI_API (Customer Processing APIs).

Кредиторы: 
  • пакет - VMD_INTERFACE (Vendor Interface), 
  • основной класс - VMD_EI_API (Vendor Processing APIs).
Подробнее...

15 июля 2015 г.

FM Для вычисления разницы между двумя датами в минутах

CALL FUNCTION 'DELTA_TIME_DAY_HOUR'
  EXPORTING
    T1      = l_curr_time
    T2      = l_time
    D1      = l_curr_date
    D2      = l_date
  IMPORTING
    MINUTES = l_minutes. Подробнее...

15 мая 2013 г.

WorkFlow и рассылка сообщений

В SAP есть такой интересный механизм, как WorkFlow. Ну очень интересный, с большим количеством ну очень интересных подводных камней.
Начинаем серию публикаций об этих самых камнях. Предполагается, что возможности самого WF вам известны.
Возможно некоторые проблемы с WF возникают у нас из-за того, что мы неправильно его готовим и есть штатные способы их решить. Но так как я этих способов не знаю - для меня эти проблемы реальны.

Первая тем - отправка сообщений из WF о ходе его продвижения.

В WF есть готовы шаг по рассылки почтового сообщения на SAP-почту. Там довольно легко в мастере мышкой можно набрать что-то вроде "эй, мистер ZZZ, для заказа N XXX нужно бы цены пониже, согласуй?!".
Сразу подсказка: если создать почтовую задачу, а потом добавить в WF её же, но в качестве обычной задачи, то можно отправлять письмо на внешнюю почту.

Но есть у этого способа целый ряд существенных недостатков:
1. Весь WF вываливается в логическую ошибку если получатель не нашёлся, или даже нашёлся, но заблокирован. Возможно это как-то можно обойти, но я такого способа не знаю.
2. Если нужно подробное письмо с кучей данных о заказе (дебитор, сумма заказа и тд) - эти данные надо сначала получить в контейнер WF. А это ещё одна задача в потоке. Тоже касается получателей, если это не инициатор, а кто-то другой, его тоже надо выяснить специальной задачей.
3. Иногда отправлять надо только если выполняется какое-то условие, либо текст/получатель зависит от какого-то условия. Придётся добавлять ещё и шаги ветвления по этому условию. Возможно и задачу отправки придётся дублировать.
4. Иногда надо отправить сообщение разными способами (сап-почта, email, sms). Соответственно и задач будет несколько.
5. Иногда надо отправить задачи нескольким разным получателям с разным текстом. Ещё дополнительные шаги.
6. Эти шаги могут повторяться с небольшими отклонениями в нескольких различных WF. Копипаста логики.
7. Выходит, что этот замечательный мастер создания письма надо проходить множество раз, а он не такой уж и элементарный. Плюс когда что-то меняется надо не забыть поменять везде одинаково.
8. WF становится очень громоздким - на один шаг-действие приходится целая пачка вспомогательных шагов. Да и в контейнере потока чёрт ногу сломит.
9. Вся эта информация о работе WF, куча лишних шагов и переменных, это всё хранится в бд, что не лучшим способом сказывается на быстродействии.
10. Много кода по получению доп. данных, который надо сопровождать. Актуально только для тех переменных, которые получаются только для письме, но не влияют на сам бизнес-процесс.
11. Если вам понадобится какая-то более сложная структура письма, типа список строк заказа с несколькими параметрами, или письмо в формате HTML с красивыми табличками, картинками и сложным форматированием, или вообще приложенный PDF - у вас это всё скорее всего не выйдет или выйдет очень сложно.

Для борьбы с этим был придуман такой приём.
- Для всего WF (или даже группы родственных WF) для отправки сообщений создаётся один ФМ (ну или метод класса) и оборачивающая её задача WF.
- В задачу эту прокидывается минимально необходимый объём данных из контейнера (в нашем случае это только номер сбытового заказа).
- Ещё одним параметром будет "код события". То есть что только что случилось и о чём надо сообщить всех заинтересованных лиц.
- В самом ВФ везде, где надо что-то отправить, вставляется шаг с этой задачей с уникальным текстом в "код события". Такие шаги можно добавлять очень быстро, а поддержки больше не требуют.
- При желании в некоторых случаях можно встраивать вызов этого ФМ в сам код задачи-действия, а шаг в WF не делать. Тогда WF будет ещё менее громоздким.
Вся же умная логика, по отправке сосредоточена в одном ФМ/классе. А уже там вы можете делать что угодно, насколько гибко/просто/наглядно как вы хотите.
В моём случае это:
- в зависимости от события, вида заказа, различных условий вычисляемых по номеру заказа, выбираются типы получателей (например, "автор заказа", "менеджер дебитора", "логист по закупу материала", "транспортный менеджер", прочие роли в процессе) и способы отправки (сап-почта, email, sms).
- по заказу и типу получателя находятся адреса (имя получателя сап-почты, email, телефон)
- по заказу ищутся все необходимые данные для текста и делаются все необходимые проверки.
- по заказу и типу сообщения формируется текст сообщения. причём возможности тут гораздо богаче, чем в мастере.
- при желании можно приаттачить что угодно, например, выходную печатную форму документа.
- планируется добавить логирование всех этих данных, чтобы можно было посмотреть что кому когда уходило. в журнале WF всё не так наглядно.
И вся логика работы в пределах нескольких методов одного класса. Очень наглядно. Очень удобно сопровождать.
И WF выглядит гораздо понятнее. Что тоже очень большой плюс.
Подробнее...

29 сентября 2011 г.

ABAP: как узнать размер окна SAP экрана в писелях

DATA: ref_consumer TYPE REF TO cl_gui_props_consumer,
      s_metric_factors TYPE CNTL_METRIC_FACTORS.


ref_consumer = cl_gui_props_consumer=>create_consumer( ).
s_metric_factors = ref_consumer->get_metric_factors( ).

s_metric_factors-screen-x - ширина
s_metric_factors-screen-y - высота

подсмотрено здесь. Подробнее...

13 сентября 2011 г.

Позновательно о TabStrip

Помимо того, что написано в ссылке:
Закладка также может быть полем вывода. То есть если объявить переменную с таким же именем и менять её значение - оно будет подставлять в заголовок закладки. Только саму галку "поле вывода" не забудьте поставить. Подробнее...

30 августа 2011 г.

Принудительное подтверждение изменений в ALV GRID

Проблема: В ALV GRID-е чтобы отработали события data_changed и data_changed_finished необходимо нажимать enter после редактирования. С числами и текстом это более менее ожидаемо и проблем не возникает. А вот со списками drop-down это совсем неочевидно. Поэтому следующее действие (например, сохранение введённых данных) отрабатывает без учёта этих изменений.

Решение:
IF l_grid_items IS NOT INITIAL.
CALL METHOD l_grid_items->check_changed_data.
ENDIF.
Этот код заканчивает редактирование ALV GRID-а. В моем случае его оказалось достаточно вписать в PAI до обработки параметра user-command.
Подробнее...

29 июля 2011 г.

Поиск в исходных кодах

Уже писал раньше про ФМ RPR_ABAP_SOURCE_SCAN, который помогает найти в исходных кода текст
Оказалось, что он ище далеко не везде. Зато RS_ABAP_SOURCE_SCAN ищет и в классах, и в расширениях cmod. Подробнее...

13 июля 2011 г.

Партии VS Виды оценки

Оказывается "партии" (charg) и "Виды оценки" (bwtar) - совсем не одно и то же, как меня когда-то убедил кто-то из консультантов.

На сбытовых заводах у нас они соответствуют 1к1, а, например, на балансовых - не соответствуют.

Соответствие можно восстановить в таблице mcha. Там по материалу/заводу/"партии" можно узнать вид оценки. Подробнее...

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.
Подробнее...