Ренцо Колле
Ральф Дентцер
Ян Храстник
Прочитать позже
432
Эта глава посвящена использованию моделей Core Data Services (CDS) для
транзакционных приложений. Это приложения, которые не только считывают данные,
но и изменяют их.
Транзакционные приложения и
инфраструктура в SAP S/4HANA*
Показано, как определить транзакционное приложение. Описаны соответствующие
особенности, которые необходимо учесть, описаны. Показано, как работает
транзакционная инфраструктура в SAP S/4HANA, как определить модели
транзакционных объектов и реализовать их бизнес-логику.
*Оригинал (англ.): Core Data Services for ABAP. Ренцо Колле, Ральф Дентцер, Ян
Храстник. Издательство SAP PRESS. Разделы 9.1, 9.2. 2019, с. 325–330.
Бизнес-объекты
Бизнес-объекты логически объединяют несколько сущностей (entity). В качестве примера
бизнес-объекта мы уже рассматривали заказ клиента, который состоит, помимо прочего,
из данных заголовка, данных позиций и графика поставок. Между ними установлены
композиционные взаимосвязи, поэтому экзистенциально они не зависят от
соответствующей родительской сущности. Несмотря на то, что для чтения бизнес-
объекты играют второстепенную роль, они формируют контекст, который очень важен
при записи. Обычно несколько сущностей одного бизнес-объекта обрабатываются и
записываются вместе. Другие упоминаемые или привязанные сущности часто только
считываются или имеют собственную реализацию для доступа с правом записи.
Полномочия и блокировка
Помимо выделенных средств управления полномочиями для записи данных также
требуются эксклюзивные блокировки, которые обеспечивают транзакционную
непротиворечивость данных. В большинстве случаев поведение средств проверки
полномочий и блокировок реализуется на уровне бизнес-объекта в целом, а не на уровне
отдельной подсущности бизнес-объекта. Лишь в самых редких случаях бизнес-объекты
поддерживают блокировку на уровне подсущности.
Бизнес-логика
Обычно при записи данные, введённые пользователем или переданные через машинный
интерфейс, сохраняются не сразу. Сначала выполняется проверка введённых данных.
Другие данные выбираются или вычисляются, что зависит от изменений в данных или от
бизнес-конфигурации и т. д. Все эти правила и условия называются общим термином
бизнес-логика. Изменяются не только данные бизнес-объекта, к которому выполняется
обращение. Во многих случаях также инициируются и обрабатываются другие бизнес-
объекты и даже целые бизнес-процессы или последующие процессы.
Действия
Для изменения данных и бизнес-объектов можно использовать элементарные операции:
создание, изменение или удаление (вместе с чтением они называются операциями
управления данными (CRUD)). Однако бизнес-логика часто выводится в более
комплексных операциях, которые содержат несколько изменений, обеспечивая
непротиворечивость обработки операций. Операции такого типа вызываются в
следующих действиях. Действия помогают снять с конечного пользователя или
потребителя ответственность за бизнес-логику или непротиворечивость множественных
изменений. Они обеспечивают повышенное удобство при считывании повторяющихся
изменений. Если требуется, действия также могут явно контролировать расширенные
полномочия. Рассмотрим это в Раздел 9.3.6 на примере.
Транзакционная инфраструктура
в SAP S/4HANA (раздел 9.2)
Сервер приложений или база данных
На этом этапе может возникнуть вопрос, на каком уровне должна быть реализована и
интегрирована бизнес-логика. В среде ABAP ответ достаточно однозначен: бизнес-логика
(в том понимании, в котором мы говорим о ней здесь) выполняется на сервере
приложений и реализуется в ABAP. Это обусловлено масштабируемостью серверов
приложений и подробной бизнес-логикой SAP S/4HANA, которая, разумеется, должна
быть понятной и применяемой. Такой подход не противоречит цели перевода логики с
обработкой большого объёма данных в базу данных. В оптимальном варианте базу
данных SAP HANA следует использовать, разумеется, для всех операций чтения с
мэппингом трансформаций, насколько возможно, посредством SQL при чтении данных.
Этот подход разумным образом также можно применять в транзакционной логике.
CDS и BOPF
Платформа ABAP позволяет использовать модели CDS в качестве базы для
моделирования транзакционных приложений. В этом контексте необходимо учитывать
все требования, определённые в предыдущем разделе для транзакционных приложений
или соответствующей инфраструктуры. Для этого в транзакционную среду выполнения
интегрирована структура обработки бизнес-объектов (Business Object Processing
Framework; BOPF), которая является частью платформы ABAP и учитывает все эти
аспекты. На момент написания этой книги (весна 2019 года) это единственный способ
интеграции транзакционной логики в CDS, который поддерживается платформой ABAP
для внедрения приложения на базе CDS. Интеграция других транзакционных структур
или реализаций за пределами BOPF в настоящее время не поддерживается, но
планируется в будущих версиях (а именно, модель программирования ABAP RESTful).
При создании сервисов OData (Раздел 9.4.2) транзакционную логику можно реализовать
непосредственно в процессе внедрения сервиса, а с помощью модели CDS определить
доступ для чтения. Мы не будем останавливаться на этой теме, а рассмотрим подробнее
сценарий, создаваемый с нуля.
В рамках описанного подхода бизнес-объект BOPF создаётся на основе моделей CDS. Для
полноты картины здесь следует заметить, что модели BOPF, созданные таким способом, в
настоящее время не предоставляют полный диапазон функций и возможностей BOPF.
Однако они постоянно расширяются.
Дополнительные ресурсы
Главное правило заключается в том, что моделирование данных или объектов для бизнес-
объекта и его свойств выполняется посредством CDS. Изменить эту информацию в
модели BOPF, созданной на их основе, невозможно. Моделирование поведения, т. е.
определение бизнес-логики (средства выбора и вычисления данных, а также проверки
непротиворечивости и действия), выполняется в модели BOPF.
Среда выполнения BOPF, которая работает на базе модели BOPF, обеспечивает полную
поддержку операций управления данными (CRUD) и чётко определённых интерфейсов
для интеграции проверок полномочий, блокировок и бизнес-логики с возможностью
последующей реализации.
SADL и OData
Мы будем рассматривать работу с приложениями через веб-стандарты REST и OData,
поскольку платформа ABAP уже предоставляет реальную сквозную поддержку этих
стандартов. Например, в данном случае инфраструктура обрабатывает не только
указанные выше ключи, но и обеспечивает маршрутизацию обращений с правом чтения и
записи. Все нетранзакционные запросы на чтение и аналитические запросы делегируются
напрямую базе данных SAP HANA через интерфейс Open SQL. Транзакционные запросы
на чтение и запись данных передаются в транзакционную среду выполнения (BOPF) и
транзакционный буфер сервера приложений. Таким образом, сочетается оптимальное
использование базы данных SAP HANA с обработкой всех постоянных данных при всех
преимуществах транзакционной среды выполнения на сервере приложений ABAP и
возможности применения существующей бизнес-логики.
Для перевода запросов GET OData в запросы SQL используется инфраструктура Service
Adaptation Definition Language (SADL) на платформе ABAP. Инфраструктура SADL
разработана для чтения и обработки данных на базе моделей.
Дополнительные ресурсы
Для получения дополнительной информации о SADL посетите портал SAP Help Portal
по адресу https:// help.sap.com и введите в строке поиска Consuming Business Entities
with SADL (http://s-prs.co/v482202).
@AbapCatalog.sqlViewName: 'ZISALESORDERTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@ObjectModel.modelCategory: #BUSINESS_OBJECT
@ObjectModel.compositionRoot: true
as selectfrom ZI_SalesOrder
on $projection.SalesOrder = _SalesOrderItem.SalesOrder
{
key SalesOrder,
SalesOrderType,
SalesOrganization,
DistributionChannel,
OrganizationDivision,
DeliveryStatus,
DeletionIndicator,
CreatedByUser,
CreationDateTime,
LastChangedByUser,
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
_SalesOrderItem
@ObjectModel.compositionRoot: true
@ObjectModel.association.type: #TO_COMPOSITION_CHILD
Если вы знакомы с BOPF, вы, вероятно, знаете, что здесь также можно выполнить
обратные привязки композиции к родительскому узлу и корню. Поскольку такие привязки
целесообразно настроить для навигации, их следует пометить следующей аннотацией:
@ObjectModel.association.type: #TO_COMPOSITION_PARENT
@ObjectModel.association.type: #TO_COMPOSITION_ROOT
Чтобы явно пометить бизнес-объект как таковой, используйте следующую аннотацию для
корневой сущности, представляющей бизнес-объект:
@ObjectModel.modelCategory: #BUSINESS_OBJECT
Эта аннотация не является релевантной во время выполнения и имеет значение только для
семантической категоризации в контексте VDM.
Уровень транзакционных ракурсов
Пример: бизнес-объект
Итак, определим модель бизнес-объекта для заказа клиента. Поскольку в основе ракурсов
CDS лежат уже определённые базовые ракурсы интерфейса, известные аннотации к
соответствующим полям (их семантике) доступны автоматически через логику
распространения аннотаций. Кроме того, для имён полей уже определены подходящие
псевдонимы.
Определите заголовок заказа клиента, как показано в Листинге 9.1. Здесь уже имеется
дочерняя привязка к позиции заказа клиента, определённой в Листинге 9.2. Новые
аннотации выделяются соответствующим образом.
@AbapCatalog.sqlViewName: 'ZISALESORDERITTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderItem'
define view ZI_SalesOrderItemTP
as selectfrom ZI_SalesOrderItem
association [1..1] to ZI_SalesOrderTP as _SalesOrder
on $projection.SalesOrder = _SalesOrder.SalesOrder
association [0..*] to ZI_SalesOrderScheduleLineTP
as _SalesOrderScheduleLine
on $projection.SalesOrder =
_SalesOrderScheduleLine.SalesOrder
and $projection.SalesOrderItem =
_SalesOrderScheduleLine.SalesOrderItem
{
key SalesOrder,
key SalesOrderItem,
Product,
OrderQuantity,
OrderQuantityUnit,
NetAmount,
TransactionCurrency,
CreatedByUser,
CreationDateTime,
LastChangedByUser,
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT,
#TO_COMPOSITION_ROOT]
_SalesOrder,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
_SalesOrderScheduleLine
}
@AbapCatalog.sqlViewName: 'ZISALESORDERSLTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderScheduleLine'
define view ZI_SalesOrderScheduleLineTP
as selectfrom ZI_SalesOrderScheduleLine
association [1..1] to ZI_SalesOrderTP as _SalesOrder
on $projection.SalesOrder = _SalesOrder.SalesOrder
association [1..1] to ZI_SalesOrderItemTP as _SalesOrderItem
on $projection.SalesOrder = _SalesOrderItem.SalesOrder
and $projection.SalesOrderItem =
_SalesOrderItem.SalesOrderItem
{
key SalesOrder,
key SalesOrderItem,
key SalesOrderScheduleLine,
DeliveryDate,
OrderQuantity,
OrderQuantityUnit,
CreatedByUser,
CreationDateTime,
LastChangedByUser,
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_ROOT]
_SalesOrder,
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT]
_SalesOrderItem
}
Однако, как было сказано выше, при работе с ракурсами CDS встречаются объекты базы
данных, которые изначально разрешают только доступ к данным в режиме чтения.
Теоретически в простых случаях можно установить доступ с записью в корректные
таблицы согласно определению ракурса с учётом аспектов создания ракурса и присвоения
ему псевдонима. Однако в инфраструктуре всегда должно присутствовать явное указание
таблицы базы данных, в которую выполняется запись. Эту таблицу необходимо указать
для каждой сущности с помощью аннотации @ObjectModel.writeActivePersistence:’<имя
таблицы базы данных>’. Указанная таблица базы данных должна иметь такую же
структуру, как и ракурс CDS, или быть совместимой с ним. Также требуются
соответствующие имена полей (в верхнем регистре согласно ABAP).
@AbapCatalog.sqlViewName: 'ZISALESORDERTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order'
@ObjectModel.modelCategory: #BUSINESS_OBJECT
@ObjectModel.compositionRoot: true
@ObjectModel.transactionalProcessingEnabled: true
@ObjectModel.writeActivePersistence: 'ZSALESORDER'
define view ZI_SalesOrderTP
…
@AbapCatalog.sqlViewName: 'ZISALESORDERITTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderItem'
@ObjectModel.writeActivePersistence: 'ZSALESORDERITEM'
define view ZI_SalesOrderItemTP
…
Листинг. 9.5. Ракурс CDS для позиции заказа клиента с транзакционными аннотациями
@AbapCatalog.sqlViewName: 'ZISALESORDERSLTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderScheduleLine'
@ObjectModel.writeActivePersistence: 'ZSALESORDERSLINE'
define view ZI_SalesOrderScheduleLineTP
…
Листинг. 9.6. Ракурс CDS для графика поставки по заказу клиента с транзакционными
аннотациями
Модель BOPF
Если вы добавили эти аннотации, при активации корневого ракурса CDS автоматически
создаётся бизнес-объект BOPF. В результате активируется транзакционная среда
выполнения. Если установить курсор на пиктограмме слева от аннотации, появится
информация о созданном объекте (см. Рис. 9.2). Для прямого перехода к бизнес-объекту
BOPF используйте отображаемую здесь ссылку «Бизнес-объект» (Business Object).
На Рис. 9.5 показан пример корневого узла. Здесь очень хорошо видно, что автоматически
создаётся не только сам бизнес-объект BOPF, но и различные объекты ABAP-словаря
(ABAP DDIC), а также классы ABAP.
Тестовую среду можно запустить напрямую с помощью ABAP Development Tools (ADT) в
Eclipse для бизнес-объекта BOPF в древовидной структуре. Для этого
выберите «Выполнить • Выполнить как • Тестовая среда» (Run • Run As • Test
Environment). С помощью этого инструмента также можно осуществлять навигацию по
всей модели. Указанные в Раздел 9.2 технические ключи не входят в модель CDS
(поскольку эта модель работает с семантическими ключами).
Загрузка экземпляров узлов в тестовую среду
@ObjectModel.createEnabled:true
Эта аннотация используется для создания экземпляров сущности.
@ObjectModel.updateEnabled:true
@ObjectModel.deleteEnabled:true
@ObjectModel.readOnly:true
Эта аннотация используется для пометки полей как доступных только для чтения.
При создании и изменении все поля можно сначала записать. Этой аннотацией
помечаются поля, вычисляемые или определяемые внутренним образом, без
возможности изменения пользователем напрямую.
@ObjectModel.mandatory:true
@AbapCatalog.sqlViewName: 'ZISALESORDERTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order'
@ObjectModel.modelCategory: #BUSINESS_OBJECT
@ObjectModel.compositionRoot:true
@ObjectModel.transactionalProcessingEnabled:true
@ObjectModel.writeActivePersistence: 'ZSALESORDER'
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: false
define view ZI_SalesOrderTP
as selectfrom ZI_SalesOrder
association [0..*] to ZI_SalesOrderItemTP as _SalesOrderItem
on $projection.SalesOrder = _SalesOrderItem.SalesOrder
{
@ObjectModel.readOnly: true
key SalesOrder,
@ObjectModel.mandatory: true
SalesOrderType,
@ObjectModel.mandatory: true
SalesOrganization,
@ObjectModel.mandatory: true
DistributionChannel,
@ObjectModel.mandatory: true
OrganizationDivision,
DeliveryStatus,
@ObjectModel.readOnly: true
DeletionIndicator,
@ObjectModel.readOnly: true
CreatedByUser,
@ObjectModel.readOnly: true
CreationDateTime,
@ObjectModel.readOnly: true
LastChangedByUser,
@ObjectModel.readOnly: true
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
_SalesOrderItem
}
@AbapCatalog.sqlViewName: 'ZISALESORDERITTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderItem'
@ObjectModel.writeActivePersistence: 'ZSALESORDERITEM'
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: true
define view ZI_SalesOrderItemTP
as selectfrom ZI_SalesOrderItem
association [1..1] to ZI_SalesOrderTP as _SalesOrder
on $projection.SalesOrder = _SalesOrder.SalesOrder
association [0..*] to ZI_SalesOrderScheduleLineTP
as _SalesOrderScheduleLine
on $projection.SalesOrder
= _SalesOrderScheduleLine.SalesOrder
and $projection.SalesOrderItem
= _SalesOrderScheduleLine.SalesOrderItem
{
@ObjectModel.readOnly: true
key SalesOrder,
@ObjectModel.mandatory: true
key SalesOrderItem,
@ObjectModel.mandatory: true
Product,
@ObjectModel.mandatory: true
OrderQuantity,
@ObjectModel.mandatory: true
OrderQuantityUnit,
NetAmount,
TransactionCurrency,
@ObjectModel.readOnly: true
CreatedByUser,
@ObjectModel.readOnly: true
CreationDateTime,
@ObjectModel.readOnly: true
LastChangedByUser,
@ObjectModel.readOnly: true
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT,
#TO_COMPOSITION_ROOT]
_SalesOrder,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
_SalesOrderScheduleLine
}
Листинг. 9.8. Ракурс CDS для позиции заказа клиента со статическими транзакционными
свойствами
@AbapCatalog.sqlViewName: 'ZISALESORDERSLTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderScheduleLine'
@ObjectModel.writeActivePersistence: 'ZSALESORDERSLINE'
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: true
define view ZI_SalesOrderScheduleLineTP
as selectfrom ZI_SalesOrderScheduleLine
association [1..1] to ZI_SalesOrderTP as _SalesOrder
on $projection.SalesOrder = _SalesOrder.SalesOrder
association [1..1] to ZI_SalesOrderItemTP as _SalesOrderItem
on $projection.SalesOrder = _SalesOrderItem.SalesOrder
and $projection.SalesOrderItem = _SalesOrderItem.SalesOrderItem
{
@ObjectModel.readOnly: true
key SalesOrder,
@ObjectModel.readOnly: true
key SalesOrderItem,
@ObjectModel.mandatory: true
key SalesOrderScheduleLine,
@ObjectModel.mandatory: true
DeliveryDate,
@ObjectModel.mandatory: true
OrderQuantity,
@ObjectModel.mandatory: true
OrderQuantityUnit,
@ObjectModel.readOnly: true
CreatedByUser,
@ObjectModel.readOnly: true
CreationDateTime,
@ObjectModel.readOnly: true
LastChangedByUser,
@ObjectModel.readOnly: true
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_ROOT]
_SalesOrder,
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT]
_SalesOrderItem
}
Листинг. 9.9. Ракурс CDS для графика поставок по заказу клиента со статическими
транзакционными свойствами
Результаты на примере
Если взглянуть на бизнес-объект BOPF после активации изменений, вы увидите, что
аннотированные свойства также присутствуют. Свойства сущности определяют
возможности её создания, изменения и удаления пользователем и копируются в узел
BOPF, как показано на Рис. 9.7.
Рис. 9.7. Бизнес-объект BOPF: узел со статическими свойствами
Сквозная поддержка
Теперь ракурсы CDS можно использовать для нетранзакционного чтения, а
инфраструктуру BOPF для транзакционного чтения и записи данных. Как было сказано в
Раздел 9.2, на сегодняшний день на платформе ABAP для интегрированной среды
выполнения интерфейс программирования недоступен. Поэтому
пользователь/разработчик принимает решение о том, как предоставляется доступ для
чтения: напрямую через CDS/SQL к базе данных (для неизменённых сохраненённых
данных) или транзакционный доступ для чтения необходимо инициировать в
транзакционной среде выполнения, если требуется учитывать транзакционные изменения.
Кроме того, пользователь/разработчик должен понимать, как BOPF обрабатывает ключи,
если в модели применяются семантические ключи. При использовании моделей в
сервисах OData платформа ABAP предлагает сквозную поддержку, которая
рассматривается в Раздел 9.4, в рамках выделенного использования моделей сервисов.
METHOD /bobf/if_lib_lock_active~lock_active_entity.
DATA lt_sales_order TYPE ztisalesordertp.
io_read->retrieve( EXPORTING iv_node =is_ctx-node_key
it_key =it_key
IMPORTING et_data =lt_sales_order).
LOOP AT lt_sales_order
ASSIGNING FIELD-SYMBOL(<ls_sales_order>)
WHERE salesorder IS NOTINITIAL.
CALL FUNCTION 'ENQUEUE_ZSALESORDER'
EXPORTING
salesorder =<ls_sales_order>-salesorder
EXCEPTIONS
foreign_lock = 1
system_failure = 2
OTHERS = 3.
IF sy-subrc <> 0.
APPEND INITIALLINETO et_failed_key
ASSIGNING FIELD-SYMBOL(<ls_failed_key>).
<ls_failed_key>-key = <ls_sales_order>-key.
ENDIF.
ENDLOOP.
ENDMETHOD.
Администрирование блокировок
Далее реализация блокировки вызывается автоматически посредством BOPF. Это можно
легко проверить в тестовой среде BOPF и с помощью данных администрирования
блокировок в транзакции SM12 (см. Рис. 9.10).
Рис. 9.10. ABAP-список записей блокировки
Рис. 9.11. Тестовая среда BOPF с сообщением об ошибке при конфликте блокировки
В следующих разделах рассмотрим первые два типа. Поскольку такие побочные эффекты
моделируются и реализуются полностью в бизнес-объекте BOPF, а не в контексте
моделирования CDS, не будем делать этот раздел слишком объёмным. Предлагаем вам
обратиться к документации по приложениям BOPF и соответствующей технической
литературе. Далее в Раздел 9.3.7 рассматриваются изменения свойств, поскольку CDS
играет важную роль в моделировании и дополняет статические свойства, которые мы уже
изучили и определили в Раздел 9.3.3.
Условия триггера
В BOPF такие средства выбора можно до определённой степени моделировать. Вы можете
определить собственно средство выбора и изменения, которые это средство инициирует.
В BOPF это называется условием триггера (trigger condition). В этой структуре побочный
эффект и вызов реализующего класса происходят только в том случае, если
соответствующее изменение релевантно для выбора. Инициировать изменения можно в
конкретном экземпляре (в примере это почтовый индекс и город) или других связанных
экземплярах (в примере с корзиной покупок, когда данные заголовка изменяются в случае
изменений в позициях). Если изменения в других экземплярах выступают в роли условий
триггера, должен быть указан путь привязки от инициирующего изменения к узлу, для
которого определён вариант выбора. Также можно смоделировать тип изменений
(создание, изменение или удаление экземпляра), на которые требуется реагировать.
До сохранения данных
METHOD /bobf/if_frw_determination~execute.
DATA ls_salesorder TYPE REFTO zsisalesordertp.
DATA ls_msg TYPE symsg.
LOOP AT it_key ASSIGNING FIELD-SYMBOL(<ls_key>).
CREATE DATA ls_salesorder.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZSALESORD'
IMPORTING
number = ls_salesorder->salesorder
EXCEPTIONS
interval_not_found = 1
number_range_not_intern = 2
object_not_found = 3
quantity_is_0 = 4
quantity_is_not_1 = 5
interval_overflow = 6
buffer_overflow = 7
OTHERS = 8.
IF sy-subrc <> 0.
IF eo_message IS NOTBOUND.
eo_message =/bobf/cl_frw_factory=>get_message( ).
ENDIF.
ls_msg-msgid =sy-msgid.
ls_msg-msgno =sy-msgno.
ls_msg-msgty =sy-msgty.
ls_msg-msgv1 =sy-msgv1.
ls_msg-msgv2 =sy-msgv2.
ls_msg-msgv3 =sy-msgv3.
ls_msg-msgv4 =sy-msgv4.
eo_message->add_message(
EXPORTING
is_msg =ls_msg
iv_node =is_ctx-node_key
iv_key =<ls_key>-key
iv_attribute = 'SALESORDER'
iv_lifetime =/bobf/cm_lib=>co_lifetime_transition
).
ENDIF.
io_modify->update(
EXPORTING
iv_node =is_ctx-node_key
iv_key =<ls_key>-key
is_data =ls_salesorder
it changed fields = VALUE #( ( CONV #('SALESORDER') ) )
).
ENDLOOP.
ENDMETHOD.
Листинг. 9.11. Реализация средства выбора номер документа в BOPF в среде ABAP
Зависимости
Такие средства выбора реализуются так же, как и для номера документа. Обратите
внимание на то, что важно выполнить ведение зависимостей между средствами выбора.
Если заголовок заказа клиента и одна или несколько позиций заказа клиента (или график
поставки по заказу клиента) создаются в одном запросе, важно сначала присвоить номер
документа (в заголовке заказа клиента), а затем скопировать в связанные позиции. После
этого номер документа и номера позиций (установленные внешне или предложенные
внутри системы) копируются в (связанные) графики поставки. Ведение зависимостей
между средствами выбора можно выполнить на вкладке «Зависимость» (Dependency) в
нижней части экрана, показанного на Рис. 9.14.
Функции библиотеки
BOPF также предлагает различные функции библиотеки с использованием метаданных
модели BOPF. Для особых административных данных (дата создания, дата последнего
изменения и т. д.) ведение этой информации можно выполнить посредством
семантических аннотаций в модели CDS. В инфраструктуре ABAP эта информация
используется для автоматического добавления соответствующего варианта библиотеки к
бизнес-объекту BOPF с целью автоматического и корректного ввода данных во время
выполнения. Для этого вы уже проаннотировали поля как изменяемые, поскольку они
устанавливаются не пользователем или другим приложением, а системой.
В модели CDS для активации этой функциональности вы также добавили к этим полям в
каждой релевантной сущности семантические аннотации, выделенные в Листинге 9.12.
Листинг. 9.12. Ракурс CDS для заказа клиента с аннотациями для системных
административных данных
Условия триггера
Проверки непротиворечивости моделируются в BOPF и инициируются изменениями так
же, как и средства выбора. Это означает, что соответствующие проверки можно
выполнять автоматически при внесении релевантных изменений. Если требуется,
создаются сообщения для поддержки пользователя и обеспечения ввода
непротиворечивых данных. Обычно такие проверки требуется выполнять не только как
побочный эффект изменений, но и как явные запросы. Одной из причин для этого
является невозможность исправления ряда ошибок непосредственно в бизнес-объекте
путём изменения данных. Вместо этого требуется реакция на изменения в среде
(конфигурация системы, основные данные и другие бизнес-объекты). В BOPF доступна
опция выполнения проверки непротиворечивости по условию триггера check, что
соответствует явному требованию выполнить проверку. В модели BOPF на базе CDS это
условие триггера всегда установлено автоматически, поскольку это отвечает стандартам
передового опыта. По этой причине оно не отображается в редакторе BOPF.
Функции библиотеки
Для проверок непротиворечивости также существуют функции библиотеки. Некоторые из
них мы будем использовать в примере. Для этого определите новую проверку
непротиворечивости в бизнес-объекте BOPF, но не создавайте реализующий класс,
который вы реализуете самостоятельно. Вместо этого укажите существующий класс из
библиотеки BOPF. В соответствии с информацией в Раздел 9.3.3 вы определили
статические свойства в модели, которые также переносятся в модель BOPF. Одно из этих
статических свойств является обязательным, т. е. выражает необходимое значение для
аннотированного поля, без которого в экземпляре возникнут противоречия. Для проверки
обязательных полей в BOPF предоставляется функция библиотеки.
Результат
В тестовой среде BOPF (Рис. 9.17) отображается результат проверки для нового заказа
клиента, для которого ещё не были введены дополнительные данные. Сообщение об
ошибке не даёт сохранить данные, что обеспечивает запись в базу данных только
непротиворечивой информации.
Параметры действия
В отличие от стандартных операций, для действий можно установить параметры. Это
позволяет пользователю (например, конечному пользователю) влиять на поведение
действия в предварительно определённых границах. В пользовательском интерфейсе это
соответствует диалоговому окну пользователя после нажатия соответствующей кнопки, в
котором вводятся допустимые значения.
Результат действия
В классической структуре BOPF действия не дают результата, а выполняются с данными
бизнес-объекта и изменяют их. Напротив, модель BOPF на базе CDS в настоящее время
позволяет явно моделировать результат в действиях. Это может быть узел бизнес-объекта
BOPF или тип данных по вашему выбору. Можно использовать любые типы данных из
ABAP DDIC, включая глубокие структуры и таблицы.
Здесь тестовая среда также предоставляет мощную поддержку. Функция «Создать» (New)
создаёт класс с интерфейсом, который требуется реализовать. Реализация выполняется в
соответствии со стандартной для BOPF процедурой. Пример показан в Листинге 9.13.
METHOD /bobf/if_frw_action~execute.
DATA lt_sales_order TYPE ztisalesordertp.
DATA ls_msg TYPE symsg.
CLEAR et_failed_key.
CLEAR eo_message.
io_read->retrieve( EXPORTING iv_node =is_ctx-node_key
it_key =it_key
IMPORTING et_data =lt_sales_order).
LOOP AT lt_sales_order REFERENCE INTODATA(ls_sales_order).
CASE ls_sales_order->deliverystatus.
WHEN ' '.
io_modify->delete(
EXPORTING
iv_node =is_ctx-node_key
iv_key =ls_sales_order->key
).
WHEN 'A'.
ls_sales_order->deletionindicator =abap_true.
io_modify->update(
EXPORTING
iv_node =is_ctx-node_key
iv_key =ls_sales_order->key
is_data =ls_sales_order
it_changed_fields = VALUE #( (
CONV #('DELETIONINDICATOR')
) )
).
WHEN OTHERS.
IF eo_message IS NOTBOUND.
eo_message =/bobf/cl_frw_factory=>get_message( ).
ENDIF.
eo_message->add_cm(
NEW /bobf/cm_lib(
textid
= /bobf/cm_lib=>action_not_allowed2
severity
= /bobf/cm_lib=>co_severity_error
lifetime
= /bobf/cm_lib=>co_lifetime_transition
ms_origin_location = VALUE #(
bo_key =is_ctx-bo_key
node_key =is_ctx-node_key )
mv_object_name = 'ZI_SALESORDERTP'
mv_node_name = 'ZI_SALESORDERTP' )
).
ENDCASE.
ENDLOOP.
ENDMETHOD.
Моделирование
В Разделе 9.3.3 мы уже рассмотрели моделирование и результаты применения
статических свойств. В сущности, эти свойства можно контролировать динамически. На
уровне сущности или экземпляра можно определить возможность изменения или
удаления экземпляров. На уровне поля можно определить, готово оно к вводу или нет,
является ли обязательным, должно ли быть скрыто от пользователя по причине
нерелевантности в данном контексте (и, как правило, вследствие отсутствия присвоенного
значения). Последнее, т. е. статическое скрытие поля, конечно, не является
целесообразным, поскольку в этом случае поле вообще не стоило включать в модель.
Вообще, статические свойства модели можно дополнительно динамически ограничить
только с помощью CDS (в отличие от стандартной процедуры BOPF). Например, если
сущность невозможно удалить статически, для неё можно динамически установить
свойство deletable.
@ObjectModel.updateEnabled: #(‘EXTERNAL_CALCULATION’)
@ObjectModel.deleteEnabled: #(‘EXTERNAL_CALCULATION’)
@ObjectModel.readOnly: #(‘EXTERNAL_CALCULATION’)
@ObjectModel.mandatory: #(‘EXTERNAL_CALCULATION’)
@ObjectModel.enabled: #(‘EXTERNAL_CALCULATION’)
Пример реализации
Аннотация в модели CDS автоматически создаёт средство выбора в модели BOPF (если
оно ещё не доступно) для вычисления свойств с именем
ACTION_AND_FIELD_CONTROL, а также связанный класс, в котором можно выполнить
реализацию. Как и было сказано, реализация выполняется по стандартной процедуре в
BOPF. Пример реализации представлен в Листинге 9.15.
METHOD /bobf/if_frw_determination~execute.
DATA lt_sales_order_item TYPE ztisalesorderitemtp.
io_read->retrieve( EXPORTING iv_node =is_ctx-node_key
it_key =it_key
IMPORTING et_data =lt_sales_order_item
).
LOOP AT lt_sales_order_item
ASSIGNING FIELD-SYMBOL(<ls_sales_order_item>)
WHERE creationdatetime IS NOTINITIAL.
NEW /bobf/cl_lib_h_set_property(
is_context =is_ctx
io_modify =io_modify
)->set_attribute_read_only(
EXPORTING
iv_attribute_name = 'SALESORDERITEM'
iv_key =<ls_sales_order_item>-key
).
ENDLOOP.
ENDMETHOD.
Реализация
Как вы уже знаете из главы 4, полномочия на чтение моделируются с помощью средств
управления доступом CDS (язык управления данными, DCL) при обращении к данным
через Open SQL. Эти средства управления доступом применяются автоматически при
выборе данных с помощью ракурсов CDS. Иначе происходит при транзакционном
доступе и даже при транзакционном доступе для чтения на уровне сервисов BOPF.
Поэтому для таких вариантов доступа необходимо всегда самостоятельно добавлять
реализацию проверки полномочий. В структуре уже создан релевантный класс с двумя
методами для реализации. В методе CHECK_STATIC_AUTH можно выполнить
статическую проверку (не на основе экземпляра). Здесь проверяется возможность
выполнения пользователем определённых действий (создание, изменение или удаление
бизнес-объекта) независимо от специфичного экземпляра бизнес-объекта. Проверку на
основе экземпляра можно реализовать в методе CHECK_INSTANCE_AUTH. Эта проверка
позволяет узнать, может ли пользователь выполнять конкретные действия с одним или
несколькими экземплярами бизнес-объекта.
METHOD /bobf/if_lib_auth_draft_active~check_instance_authority.
DATA lt_sales_order TYPE ztisalesordertp.
DATA ls_key TYPE /bobf/s_frw_key.
DATA lv_textid TYPE scx_t100key.
CLEAR et_failed_key.
CLEAR eo_message.
EXIT.
IF is_ctx-node_key =zif_i_salesordertp_c=>
sc_node-zi_salesordertp.
io_read->retrieve( EXPORTING iv_node =is_ctx-node_key
it_key =it_key
IMPORTING et_data =lt_sales_order).
ELSEIF is_ctx-node_key =zif_i_salesordertp_c=>
sc_node-zi_salesorderitemtp.
io_read->retrieve_by_association(
EXPORTING
iv_node =is_ctx-node_key
it_key =it_key
iv_association =zif_i_salesordertp_c=>
sc_association-zi_salesorderitemtp-to_root
iv_fill_data =abap_true
IMPORTING
et_data =lt_sales_order
).
ELSEIF is_ctx-node_key =zif_i_salesordertp_c=>
sc_node-zi_salesorderschedulelinetp.
io_read->retrieve_by_association(
EXPORTING
iv_node =is_ctx-node_key
it_key =it_key
iv_association =zif_i_salesordertp_c=>
sc_association-zi_salesorderschedulelinetp-to_root
iv_fill_data =abap_true
IMPORTING
et_data =lt_sales_order
).
ENDIF.
LOOP AT lt_sales_order ASSIGNING FIELD-SYMBOL(<ls_sales_order>).
AUTHORITY-CHECK OBJECT 'V_VBAK_AAT'
ID 'ACTVT' FIELD is_ctx-activity
ID 'AUART' FIELD <ls_sales_order>-salesordertype.
IF sy-subrc <> 0.
ls_key-key =<ls_sales_order>-key.
APPEND ls_key TO et_failed_key.
CASE is_ctx-activity.
WHEN /bobf/cl_frw_authority_check=>sc_activity-display.
lv_textid =/bobf/cm_lib=>no_auth_display.
WHEN /bobf/cl_frw_authority_check=>sc_activity-create.
lv_textid =/bobf/cm_lib=>no_auth_create.
WHEN /bobf/cl_frw_authority_check=>sc_activity-change.
lv_textid =/bobf/cm_lib=>no_auth_update.
WHEN /bobf/cl_frw_authority_check=>sc_activity-delete.
lv_textid =/bobf/cm_lib=>no_auth_delete.
WHEN /bobf/cl_frw_authority_check=>sc_activity-execute.
lv_textid =/bobf/cm_lib=>no_auth_execute_action.
WHEN OTHERS.
RETURN.
ENDCASE.
IF eo_message IS NOTBOUND.
eo_message =/bobf/cl_frw_factory=>get_message( ).
ENDIF.
eo_message->add_cm( NEW /bobf/cm_lib(
textid =lv_textid
severity =/bobf/cm_lib=>co_severity_error
lifetime =/bobf/cm_lib=>co_lifetime_transition
ms_origin_location = VALUE #( bo_key=
is_ctx-bo_key node_key=is_ctx-node_key )
mv_object_name = 'ZI_SALESORDERTP'
mv_node_name = 'ZI_SALESORDERTP'
mv_action_name = CONV #( is_ctx-action_name )
)
).
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD /bobf/if_lib_auth_draft_active~check_static_authority.
DATA lv_textid TYPE scx_t100key.
rv_failed =abap_false.
AUTHORITY-CHECK OBJECT 'V_VBAK_AAT'
ID 'ACTVT' FIELD is_ctx-activity
ID 'AUART' DUMMY.
IF sy-subrc <> 0.
rv_failed =abap_true.
CASE is_ctx-activity.
WHEN /bobf/cl_frw_authority_check=>sc_activity-display.
lv_textid =/bobf/cm_lib=>no_auth_display.
WHEN /bobf/cl_frw_authority_check=>sc_activity-create.
lv_textid =/bobf/cm_lib=>no_auth_create.
WHEN /bobf/cl_frw_authority_check=>sc_activity-change.
lv_textid =/bobf/cm_lib=>no_auth_update.
WHEN /bobf/cl_frw_authority_check=>sc_activity-delete.
lv_textid =/bobf/cm_lib=>no_auth_delete.
WHEN /bobf/cl_frw_authority_check=>sc_activity-execute.
lv_textid =/bobf/cm_lib=>no_auth_execute_action.
WHEN OTHERS.
RETURN.
ENDCASE.
eo_message =/bobf/cl_frw_factory=>get_message( ).
eo_message->add_cm( NEW /bobf/cm_lib(
textid =lv_textid
severity =/bobf/cm_lib=>co_severity_error
lifetime =/bobf/cm_lib=>co_lifetime_transition
ms_origin_location = VALUE #( bo_key=
is_ctx-bo_key node_key=is_ctx-node_key )
mv_object_name = 'ZI_SALESORDERTP'
mv_node_name = 'ZI_SALESORDERTP'
mv_action_name = CONV #( is_ctx-action_name )
)
).
ENDIF.
ENDMETHOD.
Листинг. 9.17. Реализация проверки полномочий для экземпляра в BOPF в среде ABAP
Вычисление в ABAP
Обратите внимание на то, что для вычисляемых в базе данных полей данные
необязательно поступают напрямую из базы данных во время транзакционного доступа.
Поэтому вычисление не может выполняться только здесь. Позиции заказа клиента во
время транзакции могут изменяться. В этом случае корректное значение общей суммы
также должно быть вычислено и возвращено для транзакционного доступа. Это
применимо и в примере с полем DeliveryIsCompleted. Таким образом, по-прежнему
необходимо предоставить средство выбора, которое будет выполнять аналогичное
вычисление в BOPF и в ракурсе CDS. Несмотря на избыточность реализации, такая
процедура целесообразна во многих случаях, поскольку вычисление с помощью ABAP
выполняется только для транзакционного доступа, а вычисление с помощью CDS
выполняется в базе данных исключительно для чтения данных.
Использование бизнес-объектов в
нескольких приложениях
В этом разделе рассматриваются модели транзакционных сервисов CDS, которые выводят
приложение в специфичном сервисе. Реализовать бизнес-объект и связанную бизнес-
логику нужно только один раз, чтобы избежать избыточности и обеспечить
непротиворечивость системы. Однако одновременно требуется использовать бизнес-
объект в разных бизнес-процессах, разных приложениях и разных пользовательских
интерфейсах. Помимо этого, вам требуется выводить только определённые компоненты
транзакционной модели в специфичном интерфейсе программирования приложений (API)
или пользовательском интерфейсе. Необходимо определить ограничения как на уровне
данных, так и на уровне поведения или доступных функций.
Как было сказано выше, модель CDS и модель BOPF изначально работают во время
выполнения параллельно. В целом, пользователь вашего приложения должен иметь
представление об этой корреляции, чтобы получать доступ к данным по правильному
каналу. Разрешая определять модели сервисов на базеCDS и выполнять мэппинг
сущностей для сервисов OData, платформа ABAP предоставляет комплексную модель
программирования для непротиворечивого выполнения этой задачи.
Выводимые узлы
Статические свойства
Аннотации
Известные аннотации также используются для определения структуры объектов в модели
сервисов:
@ObjectModel.compositionRoot: true
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT]
@ObjectModel.association.type: [#TO_COMPOSITION_ROOT]
@ObjectModel.transactionalProcessingDelegated:true
@Metadata.allowExtensions: true.
В этом примере модель транзакционных объектов для заказа клиента должна выводиться
полностью как сервис. Таким образом, ракурс CDS определяется для каждой из трёх
сущностей со всеми необходимыми аннотациями. Определение заголовка заказа клиента
представлено в Листинге 9.19.
@AbapCatalog.sqlViewName: 'ZCSALESORDERTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order'
@Metadata.allowExtensions: true
@ObjectModel.compositionRoot: true
@ObjectModel.transactionalProcessingDelegated: true
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: false
define view ZC_SalesOrderTP
as selectfrom ZI_SalesOrderTP
association [0..*] to ZC_SalesOrderItemTP as _SalesOrderItem
on $projection.SalesOrder = _SalesOrderItem.SalesOrder
{
key SalesOrder,
SalesOrderType,
SalesOrganization,
DistributionChannel,
OrganizationDivision,
DeliveryStatus,
DeletionIndicator,
CreatedByUser,
CreationDateTime,
LastChangedByUser,
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
_SalesOrderItem
}
@AbapCatalog.sqlViewName: 'ZCSALESORDERITTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderItem'
@Metadata.allowExtensions: true
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: true
define view ZC_SalesOrderItemTP
as selectfrom ZI_SalesOrderItemTP
association [1..1] to ZC_SalesOrderTP as _SalesOrder
on $projection.SalesOrder = _SalesOrder.SalesOrder
association [0..*] to ZC_SalesOrderScheduleLineTP as _
SalesOrderScheduleLine
on $projection.SalesOrder = _
SalesOrderScheduleLine.SalesOrder
and $projection.SalesOrderItem = _
SalesOrderScheduleLine.SalesOrderItem
{
key SalesOrder,
key SalesOrderItem,
Product,
OrderQuantity,
OrderQuantityUnit,
NetAmount,
TransactionCurrency,
CreatedByUser,
CreationDateTime,
LastChangedByUser,
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT,
#TO_COMPOSITION_ROOT]
_SalesOrder,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
_SalesOrderScheduleLine
}
Листинг. 9.20. Ракурс CDS для позиции заказа клиента в модели сервисов
@AbapCatalog.sqlViewName: 'ZCSALESORDERSLTP'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales OrderScheduleLine'
@Metadata.allowExtensions: true
@ObjectModel.createEnabled: true
@ObjectModel.updateEnabled: true
@ObjectModel.deleteEnabled: true
define view ZC_SalesOrderScheduleLineTP
as selectfrom ZI_SalesOrderScheduleLineTP
association [1..1] to ZC_SalesOrderTP as _SalesOrder
on $projection.SalesOrder = _SalesOrder.SalesOrder
association [1..1] to ZC_SalesOrderItemTP as _SalesOrderItem
on $projection.SalesOrder = _SalesOrderItem.SalesOrder
and $projection.SalesOrderItem = _SalesOrderItem.SalesOrderItem
{
key SalesOrder,
key SalesOrderItem,
key SalesOrderScheduleLine,
DeliveryDate,
OrderQuantity,
OrderQuantityUnit,
CreatedByUser,
CreationDateTime,
LastChangedByUser,
LastChangeDateTime,
@ObjectModel.association.type: [#TO_COMPOSITION_ROOT]
_SalesOrder,
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT]
_SalesOrderItem
}
Листинг. 9.21. Ракурс CDS для графика по ставок по заказу клиента в модели сервисов
Сервис OData
Самым простым способом создания сервиса OData из модели сервисов, определённой в
предыдущем разделе, является использование аннотации @OData.publish: true.
Чтобы сервис OData можно было вызвать с помощью клиента REST или OData (например,
тестового клиента SAP Gateway), он должен быть явно активирован с использованием
транзакции /IWFND/ MAINT_SERVICE (активация сервисов и управление ими).
Метаданные OData
Созданный таким образом сервис теперь содержит все метаданные, которые можно
получить из модели сервисов на базе CDS. Например, некоторые аннотации CDS
переводятся напрямую в аннотации OData с соответствующим обогащением модели.
Таким образом, каждый ракурс CDS в OData становится сущностью OData
соответствующего типа, которая содержит поля ракурса CDS в виде свойств OData с
именем и типом, а также аналогичной информацией по ключу. Мэппинг привязок CDS
выполняется как мэппинг привязок OData или наборов привязок OData и свойств
навигации OData. Поскольку модель транзакционных объектов в дополнение к CDS
содержит действия, определённые в BOPF, их также необходимо вывести. Это
выполняется автоматически. Мэппинг действий, определённых в BOPF, выполняется как
мэппинг операций импорта функции (в OData V2). Для выбора действий, которые будут
выводиться через сервис OData, используется аннотация @ObjectModel.delegatedAction.
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy:’...’
…
@ObjectModel.readOnly: true
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_F_
SALESORDERITEM'
cast( ' ' as boole_d preserving type ) as OrderIsFreeOfCharge,
…
Листинг. 9.22. Ракурс CDS для заказа клиента в модели сервисов с виртуальным
элементом
METHOD if_sadl_exit_calc_element_read~get_calculation_info.
INSERT CONV #('NETAMOUNT')
INTO TABLE et_requested_orig_elements.
ENDMETHOD.
METHOD if_sadl_exit_calc_element_read~calculate.
LOOP AT it_original_data
ASSIGNING FIELD-SYMBOL(<ls_salesorderitem>).
ASSIGN COMPONENT 'NETAMOUNT'
OF STRUCTURE <ls_salesorderitem>
TO FIELD-SYMBOL(<lv_netamount>).
CHECK sy-subrc = 0.
CHECK <lv_netamount> IS INITIAL.
ASSIGN COMPONENT 'ORDERISFREEOFCHARGE'
OF STRUCTURE ct_calculated_data[ sy-tabix ]
TO FIELD-SYMBOL(<lv_orderisfreeofcharge>).
CHECK sy-subrc = 0.
<lv_orderisfreeofcharge> =abap_true.
ENDLOOP.
ENDMETHOD.
@UI.selectionField
@UI.lineItem
@UI.identification
@UI.headerInfo;
@UI.facet;
@UI.fieldGroup.
@UI.lineItem: {
type:#FOR_ACTION,
dataAction:’BOPF:<name of action>’,
@UI.identification: {
type:#FOR_ACTION,
dataAction:’BOPF:<name of action>’,
@Metadata.layer: #CUSTOMER
@UI.headerInfo.typeName: 'Sales Order'
@UI.headerInfo.typeNamePlural: 'Sales Orders'
@UI.headerInfo.title.label: 'Sales Order'
@UI.headerInfo.title.value: 'SalesOrder'
annotate view ZC_SalesOrderTP with
{
@UI.identification: [{position: 10, importance: #HIGH},
{type:#FOR_ACTION, position: 0,
dataAction: 'BOPF:DELETE',
label: 'Delete SalesOrder' }]
@UI.lineItem: [{position: 10, importance: #HIGH}]
@UI.fieldGroup: [{qualifier: 'GeneralData', position: 10,
importance: #HIGH }]
@UI.selectionField: [{position: 10}]
SalesOrder;
@UI.identification: [{position: 20, importance: #HIGH}]
@UI.lineItem: [{position: 20, importance: #HIGH}]
@UI.fieldGroup: [{qualifier: 'GeneralData', position: 20,
importance: #HIGH }]
@UI.selectionField: [{position: 20}]
SalesOrderType;
@UI.identification: [{position: 20, importance: #HIGH}]
@UI.lineItem: [{position: 20, importance: #HIGH}]
@UI.fieldGroup: [{qualifier: 'GeneralData', position: 30,
importance: #HIGH }]
@UI.selectionField: [{position: 20}]
SalesOrganization;
@UI.identification: [{position: 30, importance: #HIGH}]
@UI.lineItem: [{position: 30, importance: #HIGH}]
@UI.fieldGroup: [{qualifier: 'GeneralData', position: 40,
importance: #HIGH }]
@UI.selectionField: [{position: 30}]
DistributionChannel;
@UI.identification: [{position: 40, importance: #HIGH}]
@UI.lineItem: [{position: 40, importance: #HIGH}]
@UI.fieldGroup: [{qualifier: 'GeneralData', position: 50,
importance: #HIGH }]
@UI.selectionField: [{position: 40}]
OrganizationDivision;
@UI.identification: [{position: 50, importance: #HIGH}]
@UI.lineItem: [{position: 50, importance: #HIGH}]
@UI.selectionField: [{position: 50}]
DeliveryStatus;
@UI.identification: [{position: 60, importance: #LOW}]
@UI.lineItem: [{position: 60, importance: #LOW}]
DeletionIndicator;
@UI.identification: [{position: 70, importance: #LOW }]
@UI.fieldGroup: [{qualifier: 'AdminData', position: 10,
importance: #LOW }]
CreatedByUser;
@UI.identification: [{position: 80, importance: #LOW}]
@UI.fieldGroup: [{qualifier: 'AdminData', position: 20,
importance: #LOW }]
CreationDateTime;
@UI.identification: [{position: 90, importance: #LOW}]
@UI.fieldGroup: [{qualifier: 'AdminData', position: 30,
importance: #LOW }]
LastChangedByUser;
@UI.identification: [{position: 100, importance: #LOW}]
@UI.fieldGroup: [{qualifier: 'AdminData', position: 40,
importance: #LOW }]
LastChangeDateTime;
}
Листинг. 9.24. Расширение метаданных CDS для заказа клиента с аннотациями UI
Для полей выбора также доступны справки по вводу, описанные в главе 10 (см. Рис. 9.21).
Рис. 9.21. Страница объекта с элементами SAP Fiori для ввода заказа
Дополнительные ресурсы
Заключение (раздел 9.5)
В этой главе показано, как можно эффективно использовать CDS для определения и
реализации транзакционных приложений. Здесь обзорно рассматривается транзакционная
среда выполнения, а также способы активации транзакционной среды выполнения для
приложения, настройки статических и динамических свойств, реализации эксклюзивных
блокировок и проверок полномочий, реализации бизнес-логики в виде побочных
эффектов и добавления действий. Кроме того, мы изучили возможности вывода
приложения в виде сервиса OData и размещения поверх него простого пользовательского
интерфейса SAP Fiori.
Авторы
Корректура