Авторский материал от 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.
Разработка логики операции
Код операции обрабатывается драйвером в следующей последовательности:
- Из настроечной таблицы считываются классы и интерфейс.
- Вызывается метод if_pt_linkable~do_import (ZCL_PT__OUTD_IF), в котором необходимо подготовить все данные для анализа.
- Вызывается метод if_pt_executable~execute (ZCL_PT__OUTD), который вызывает статический метод операции op_outd и передаёт ей данные подготовленные на предыдущем шаге.
- В методе op_outd реализуется вся логика работы операции и изменяются внутренние таблицы или переменный аргумент ОВ.
Ограничения стандартные для ABAP Objects :
- Ссылаться на типы данных в классе можно только через TYPE. LIKE можно использовать только для объектов локальных данных и полей SY-*.
- Использование FORM..ENDFORM запрещено.
- Использование таблиц с заголовками запрещено.
Доработка интерфейса 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.