Авторский материал от ka. Нумерация вся слетела к бабушке…

Классы и интерфейс

Операции в RPTIME01 состоят из одного интерфейса и двух классов:

ZIF_PT_<имя операции>_IF  — это интерфейс, который содержит атрибуты класса, который описывает логику операции.

ZCL_PT_<имя операции> — это класс, который описывает логику работы операции. Непосредственно описание логики находится в методе OP_<имя операции> . Класс оперирует атрибутами из интерфейса ZIF_PT_<имя операции>_IF, которые из него считываются в методе IF_PT_EXECUTABLE~EXECUTE.  

ZCL_PT_<имя операции>_IF – это класс, который заполняет атрибуты интерфейса ZIF_PT_<имя операции>_IF.

Настройка

Связь имени операции с классом и интерфейсом (которые сначала надо создать) необходимо настроить ракурсе V_T7TIM_COPERA :

+ необходимо настроить саму операцию в PE04.

Вызов операции в RPTIME01

Вызов всех операций происходит в методе PROCESS_OPERATION (CL_PT_PCR_INTERPRETER), далее в GET_OPERATION_INSTANCE (CL_PT_PCR_INTERPRETER) по названию операции считывается таблица T7TIM_COPERA чтобы определить какой класс и интерфейс использовать. Судя по коду, можно переопределить классы и интерфейс для стандартных операций просто добавив их в T7TIM_COPERA!

Последовательность переходов по программе до вызова операции выглядит примерно так:

Создание шаблона пользовательской операции

Выполнение всех шагов данного пункта + настройка из п. 1.2 позволит создать «пустую» операцию, которую можно даже вставлять в схему ОВ, но при этом делать она ничего не будет. Полезна для экспериментов и дальнейшей разработки.

Создание интерфейса для операции ZIF_PT_<имя операции>_IF

Создаём интерфейс в SE24

Имя интерфейса должно быть ZIF_PT_<имя операции>_IF, например ZIF_PT__OUTD_IF для операции с именем “ _OUTD”.

Заполняем свойства интерфейса

Properties

Interfaces

Добавляем два стандартных интерфейса:

IF_PT_TIME_EVAL_OPERATION_IF

IF_PT_TIME_EVAL_OP_VARGT_IF

Attributes

Сюда необходимо добавить все атрибуты, которые будут передаваться в класс операции.

Создание класса для заполнения атрибутов интерфейса операции ZCL_PT_<имя операции>_IF

Создаём класс в SE24

Имя класса должно быть ZCL_PT_<имя операции>_IF, например ZCL_PT__OUTD_IF для операции с именем “ _OUTD”.

Заполняем свойства класса

Properties

Нажимаем кнопку Superclass и вводим имя суперкласса CL_PT_BURULE_MODULE_IF_BASE, это позволит унаследовать множество стандартных методов и атрибутов.

Interface

Добавляем:

IF_PT_TIME_EVAL_OPERATION_IF

IF_PT_TIME_EVAL_OP_VARGT_IF

ZIF_PT__OUTD_IF  — Это тот, что мы создали на предыдущем шаге.

Attributes

Тут многое должно появиться из присвоенных интерфейсов.

Methods

IF_PT_TIME_EVAL_OPERATION_IF~DO_EXPORT

    CALL METHOD me->do_export_base .

IF_PT_LINKABLE~DO_IMPOR

Создание класса для операции ZCL_PT_<имя операции>,

Создаём класс в SE24

Имя класса должно быть ZCL_PT_<имя операции>, например ZCL_PT__OUTD для операции с именем “ _OUTD”.

Заполняем свойства класса

Properties

Добавляем класс CL_PT_TIME_EVAL_UTILITIES в Forward declaration.

Interface

Добавляем IF_PT_EXECUTABLE

Attributes

Сюда необходимо добавить один атрибут с именем INTERFACE и типом ZIF_PT__WTPK_IF. Таким образом, мы храним все атрибуты нашего класса в интерфейсе, который для нашего класса сам является атрибутом.

Methods

Тут уже должны были появиться методы из интерфейса IF_PT_EXECUTABLE. Необходимо добавить свой Public метод с именем OP<имя операции>, вся логика работы операции будет описываться именно в этом методе. По мере разработки операции сюда же можно будет выносить в виде Private методов все необходимые подпрограммы.

Methods

IF_PT_EXECUTABLE~GET_INTERFACE

Реализация:

  METHOD if_pt_executable~get_interface.

    IF me->interface IS INITIAL.
      RAISE EXCEPTION TYPE cx_pt_no_interface_available.
    ENDIF.

    result ?= me->interface.

  ENDMETHOD.

IF_PT_EXECUTABLE~SET_INTERFACE

Реализация:

  METHOD if_pt_executable~set_interface.

    TRY.
        me->interface ?= im_interface.
      CATCH cx_sy_move_cast_error.
        RAISE EXCEPTION TYPE cx_pt_wrong_interface_type.
    ENDTRY.

  ENDMETHOD.

IF_PT_EXECUTABLE~EXECUTE

В этом методе происходит вызов метода операции OP_OUTD передача ему атрибутов интерфейса ZIF_PT__OUTD_IF, которые заполняются повторно определённым методом IF_PT_LINKABLE~DO_IMPORT в классе ZCL_PT__WTPK_IF.

Разработка логики операции

Код операции обрабатывается драйвером в следующей последовательности:

  1. Из настроечной таблицы считываются классы и интерфейс.
  2. Вызывается метод if_pt_linkable~do_import (ZCL_PT__OUTD_IF), в котором необходимо подготовить все данные для анализа.
  3. Вызывается метод if_pt_executable~execute (ZCL_PT__OUTD), который вызывает статический метод операции op_outd и передаёт ей данные подготовленные на предыдущем шаге.
  4. В методе op_outd реализуется вся логика работы операции и изменяются внутренние таблицы или переменный аргумент ОВ.

Ограничения стандартные для ABAP Objects :

  1. Ссылаться на типы данных в классе можно только через TYPE. LIKE можно использовать только для объектов локальных данных и полей SY-*.
  2. Использование FORM..ENDFORM запрещено.
  3. Использование таблиц с заголовками запрещено.

Доработка интерфейса ZIF_PT__OUTD_IF

Добавляем сюда данные, которые будем анализировать в операции.

INTERFACE zif_pt__outd_if
  PUBLIC .

  INTERFACES if_pt_time_eval_operation_if .
  INTERFACES if_pt_time_eval_op_vargt_if .

  DATA im_acdate TYPE datum .
  DATA im_changeable_contracts TYPE ptt_pernrs .
  DATA im_contract_p0007 TYPE ptt_contract_p0007 .
ENDINTERFACE.

Доработка класса ZCL_PT__OUTD_IF

Attributes

Добавлено:

PRIVATE SECTION.

  DATA rptime_context TYPE REF TO cl_pt_rptime_context .
  DATA contract_context TYPE REF TO cl_pt_rptime01_contract .
  DATA bsf_burul_addcon TYPE REF TO if_pt_bsf_burul_addcon .

Methods

get_context

Метод создаётся для чтения таблицы internal_context_tab в которой хранятся ссылки на кучу объектов. Ключ таблицы содержит классы и интерфейсы. Именно из этой таблицы (через ссылки на объекты) извлекаются все необходимые данные для анализа в логике операции.

Например, по ключу context_if = c_changeable_contracts_otype мы читаем ссылку на объект в котором в виде атрибута есть таблица IF_PT_BSF_BURUL_ADDCON~CHANGEABLE_CONTRACTS с числом договоров работника.

Добавляем метод в PRIVATE SECTION.

Определение:

    METHODS get_context
      IMPORTING
        !im_context_if TYPE seoclsname
      RETURNING
        VALUE(result)  TYPE REF TO if_pt_context
      RAISING
        cx_pt_import_failed .

Реализация:  

  METHOD get_context.

    DATA: l_context_entry TYPE ptr_context_reference,
          l_state         TYPE REF TO if_pt_rptime_cntx_part_state.

    READ TABLE me->rptime_context->internal_context_tab INTO
                     l_context_entry WITH KEY context_if = im_context_if.

    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE cx_pt_import_failed.
    ENDIF.
    l_state ?= l_context_entry-context.
    IF l_state->state CN ‘AO’.
      RAISE EXCEPTION TYPE cx_pt_import_failed.
    ENDIF.
    result = l_context_entry-context.

  ENDMETHOD.

get_changeable_contracts.

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

Метод считывает таблицу с числом договоров (число совместителей) сохраняет в Public атрибут интерфейса IM_CHANGEABLE_CONTRACTS, чтобы он был доступен всем методам.

Добавляем метод в PRIVATE SECTION.

Определение:

    METHODS get_changeable_contracts
      RAISING
        cx_pt_import_failed .

Реализация:

  METHOD get_changeable_contracts.

    DATA: l_context TYPE REF TO if_pt_context.

      CALL METHOD me->get_context
        EXPORTING
          im_context_if = ‘IF_PT_BSF_BURUL_ADDCON’
        RECEIVING
          result        = l_context.

      bsf_burul_addcon   ?= l_context.

    me->zif_pt__outd_if~im_changeable_contracts = bsf_burul_addcon->changeable_contracts.

  ENDMETHOD.

Переопределение метода IF_PT_LINKABLE~DO_IMPORT

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

Переопределение:

  PUBLIC SECTION.

    METHODS if_pt_linkable~do_import
        REDEFINITION .

Реализация:

    METHOD if_pt_linkable~do_import.
    DATA: l_queried_context       TYPE REF TO if_pt_context,
          l_contract              TYPE ptr_contract,
          l_contract_id           TYPE pcce_pernr,
          l_contract_p0007        TYPE ptr_contract_p0007,
          pcr_interpreter_context TYPE REF TO cl_pt_pcr_interpreter_context,
          day_ini_end_common_attr TYPE REF TO if_pt_day_ini_end_common_attr.

* Загружаем содержимое ОВ. В объекте im_context хранится огромное количество всяких данных.
    TRY.
        me->rptime_context ?= im_context.
      CATCH cx_sy_move_cast_error.
        RAISE EXCEPTION TYPE cx_pt_import_failed.
    ENDTRY.

* Для удобства расскладываем необходимые нам данные по объектам нашего класса

* Получаем список контрактов
    CALL METHOD me->get_changeable_contracts( ).

* Получаем код операции и ссылку на переменный аргумет
    CALL METHOD me->get_context
      EXPORTING
        im_context_if = ‘CL_PT_PCR_INTERPRETER_CONTEXT’
      RECEIVING
        result        = l_queried_context.

    pcr_interpreter_context   ?= l_queried_context.
    me->if_pt_time_eval_operation_if~op10 = pcr_interpreter_context->op10.
    GET REFERENCE OF pcr_interpreter_context->vargt INTO me->if_pt_time_eval_op_vargt_if~ref_vargt.

* Получаем acdate (текущую дату обработки)
    CALL METHOD me->get_context
      EXPORTING
        im_context_if = ‘IF_PT_DAY_INI_END_COMMON_ATTR’
      RECEIVING
        result        = l_queried_context.

    day_ini_end_common_attr   ?= l_queried_context.
    me->zif_pt__outd_if~im_acdate = day_ini_end_common_attr->acdate.

* Получаем P0007 для всех контрактов
    LOOP AT me->zif_pt__outd_if~im_changeable_contracts INTO l_contract_id.
      READ TABLE me->rptime_context->contracts INTO l_contract
                 WITH TABLE KEY contract_id = l_contract_id.

      me->contract_context ?= l_contract-contract.
      l_contract_p0007-contract_id = l_contract_id.
      l_contract_p0007-p0007 = me->contract_context->p0007.
      INSERT l_contract_p0007 INTO TABLE
                  me->zif_pt__outd_if~im_contract_p0007.

    ENDLOOP.

  ENDMETHOD.

Доработка класса ZCL_PT__OUTD

В данном классе реализуется непосредственно логика работы операции.

Methods

if_pt_executable~execute.

В этом методе просто вызывается статический метод операции.

Реализация:

  METHOD if_pt_executable~execute.

    TRY.
        CALL METHOD zcl_pt__outd=>op_outd
          EXPORTING
            im_op10                 = me->interface->if_pt_time_eval_operation_if~op10
            im_acdate               = me->interface->im_acdate
            im_contract_p0007       = me->interface->im_contract_p0007
            im_changeable_contracts = me->interface->im_changeable_contracts
          CHANGING
            ch_vargt                = me->interface->if_pt_time_eval_op_vargt_if~ref_vargt.
      CATCH cx_pt_execution_failed .
    ENDTRY.
    CALL METHOD me->interface->if_pt_time_eval_operation_if~do_export( ).
  ENDMETHOD.

op_outd

Собственно, тут и реализована логика выполнения операции.

Для примера, реализация анализа смены периода суммированного учета рабочего времени (СУРВ) на следующий день. В LOOP по списку контрактов считывается поле поле ИТ0007 с периодом СУРВ на дату анализа и следующий день, если они разные, то в переменный аргумент записывается ‘Y’, считывание ИТ0007 убрано в PRIVATE STATIC метод, чтобы было удобнее читать программу.

  METHOD op_outd.
    DATA:
      l_contract_id TYPE pcce_pernr,
      l_psu_now     TYPE wweek,
      l_psu_new     TYPE wweek,
      l_nextdaydate TYPE datum.

    LOOP AT im_changeable_contracts INTO l_contract_id.
      CASE im_op10+5(4).
        WHEN ‘CPSU’.
          TRY.
              zcl_pt__outd=>get_psu(
                EXPORTING
                  im_contract_id    = l_contract_id
                  im_endda          = im_acdate
                  im_begda          = im_acdate
                  im_op10           = im_op10
                  im_contract_p0007 = im_contract_p0007
                RECEIVING
                  psu               = l_psu_now ).
            CATCH cx_pt_execution_failed .
          ENDTRY.

          l_nextdaydate = im_acdate + 1.

          TRY.
              zcl_pt__outd=>get_psu(
                EXPORTING
                  im_contract_id    = l_contract_id
                  im_endda          = l_nextdaydate
                  im_begda          = l_nextdaydate
                  im_op10           = im_op10
                  im_contract_p0007 = im_contract_p0007
                RECEIVING
                  psu               = l_psu_new ).
            CATCH cx_pt_execution_failed .
          ENDTRY.

          IF l_psu_now NE l_psu_new.
            ch_vargt->* = ‘Y’.
          ELSE.
            ch_vargt->* = ‘N’.
          ENDIF.

      ENDCASE.

    ENDLOOP.

  ENDMETHOD.