【Fiori】ElementsのためのCDS + BOPF

##目的
以下の記事で使用するODataサービスを生成するためのCDS, BOPFの実装内容を記載します。
[【Fiori】List Reportのためのアノテーション](https://qiita.com/tami/items/87a4d18e9954cd4c34ee)

ここでは解説は省略しますが、CDS + BOPFの作成方法については以下の記事をご参照ください。
[【Fiori】CDSビューでFiori Appを作る](https://qiita.com/tami/items/1f63f0d3c46e3a0255e8)
[【Fiori】CDSビューでFiori Appを作る(2) UIアノテーション](https://qiita.com/tami/items/3322068524cfc30d236b)
[【Fiori】CDS + BOPFでトランザクション実行](https://qiita.com/tami/items/b1458094d16dfdc87359)
[【BOPF】Determination, Validationの実装](https://qiita.com/tami/items/71a80205d88893c69163)
[【BOPF】Actionの実装](https://qiita.com/tami/items/09107c428446f66cd3ef)
[【BOPF】ドラフト機能](https://qiita.com/tami/items/48f85d0e511282454307)

##構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/21989b20-066a-5eaa-d0a0-a26851eefa32.png)

##テーブル定義
###Sales Order Header (ZSO)

```abap
@EndUserText.label : 'Sales Order'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_CHARACTER_NUMERIC
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zso_mob67 {
  key client       : abap.clnt not null;
  key node_key     : snwd_node_key not null;
  so_id            : snwd_so_id;
  @AbapCatalog.foreignKey.screenCheck : true
  buyer_guid       : snwd_node_key
    with foreign key [0..*,1] snwd_bpa
      where client = zso_mob67.client
        and node_key = zso_mob67.buyer_guid;
  bp_id            : snwd_partner_id;
  currency_code    : snwd_curr_code;
  @Semantics.amount.currencyCode : 'zso_mob67.currency_code'
  gross_amount     : snwd_ttl_gross_amount;
  @Semantics.amount.currencyCode : 'zso_mob67.currency_code'
  net_amount       : snwd_ttl_net_amount;
  @Semantics.amount.currencyCode : 'zso_mob67.currency_code'
  tax_amount       : snwd_ttl_tax_amount;
  lifecycle_status : snwd_so_lc_status_code;
  billing_status   : snwd_so_cf_status_code;
  delivery_status  : snwd_so_or_status_code;
  overall_status   : snwd_so_oa_status_code;
  @AbapCatalog.foreignKey.screenCheck : true
  buy_contact_guid : snwd_node_key
    with foreign key [0..*,1] snwd_bpa_contact
      where client = zso_mob67.client
        and node_key = zso_mob67.buy_contact_guid;
  payment_method   : snwd_so_payment_method;
  payment_terms    : snwd_so_payment_term;
  created_at       : snwd_created_at;
  created_by       : uname;
  changed_at       : snwd_changed_at;
  changed_by       : uname;
}
```
###Sales Order Item (ZSO_I)

```abap
@EndUserText.label : 'Sales Order Item'
@AbapCatalog.enhancementCategory : #EXTENSIBLE_CHARACTER_NUMERIC
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zso_i_mob67 {
  key client    : abap.clnt not null;
  key node_key  : snwd_node_key not null;
  @AbapCatalog.foreignKey.screenCheck : true
  parent_key    : snwd_node_key
    with foreign key [0..*,1] zso_mob67
      where client = zso_i_mob67.client
        and node_key = zso_i_mob67.parent_key;
  so_item_pos   : snwd_so_item_pos;
  product_id    : snwd_product_id;
  currency_code : snwd_curr_code;
  @Semantics.amount.currencyCode : 'zso_mob67.currency_code'
  gross_amount  : snwd_ttl_gross_amount;
  @Semantics.amount.currencyCode : 'zso_mob67.currency_code'
  net_amount    : snwd_ttl_net_amount;
  @Semantics.amount.currencyCode : 'zso_mob67.currency_code'
  tax_amount    : snwd_ttl_tax_amount;
  unit          : snwd_quantity_unit;
  @Semantics.quantity.unitOfMeasure : 'zso_i_mob67.unit'
  quantity      : snwd_quantity;
}
```

##Interface View
###Sales Order Header (Z_I_SO)

```abap
@AbapCatalog.sqlViewName: 'ZISOMOB67'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Header'

@Metadata.allowExtensions: true
@OData.publish: true
@ObjectModel: {
    modelCategory: #BUSINESS_OBJECT,
    compositionRoot: true,
    transactionalProcessingEnabled: true,
    writeActivePersistence: 'zso_mob67',
    writeDraftPersistence: 'zso_mob67_d',
    semanticKey: ['so_id'],
    representativeKey: 'so_id',
    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true,
    draftEnabled: true
}
define view Z_I_SO_MOB67
  as select from zso_mob67
association [0..*] to z_i_soi_mob67 as _Item
on $projection.node_key = _Item.parent_key
{
    //snwd_so
    @ObjectModel.readOnly: true
    key node_key,
    @ObjectModel.readOnly: true
    so_id,
    @EndUserText.label: 'Created By'    
    @ObjectModel.readOnly: true
    created_by,    
    @ObjectModel.readOnly: true
    created_at,    
    @EndUserText.label: 'Changed By'
    @ObjectModel.readOnly: true
    changed_by,    
    @ObjectModel.readOnly: true
    changed_at,
    @EndUserText.label: 'Customer'
    @ObjectModel.mandatory: true
    bp_id,
    @ObjectModel.mandatory: true
    currency_code,
    @ObjectModel.readOnly: true
    gross_amount,
    @ObjectModel.readOnly: true
    net_amount,
    @ObjectModel.readOnly: true
    tax_amount,   
    lifecycle_status,
    billing_status,
    @EndUserText.label: 'Delivery Status'
    delivery_status,
    overall_status,
    buy_contact_guid,
    payment_method,
    payment_terms,
    
    @ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
    _Item
}
```

###Sales Order Item (Z_I_SOI)

```abap
@AbapCatalog.sqlViewName: 'ZISOIMOB67'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Item'

@Metadata.allowExtensions: true
@ObjectModel: {
    writeActivePersistence: 'zso_i_mob67',
    writeDraftPersistence: 'zso_i_mob67_d',
    semanticKey: ['so_item_pos'],
    representativeKey: 'so_item_pos',
    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true
}
define view z_i_soi_mob67 as select from zso_i_mob67
association [1..1] to Z_I_SO_MOB67 as _SalesOrder
on $projection.parent_key = _SalesOrder.node_key
{
    //zso_i_mob67
    @ObjectModel.readOnly: true
    key node_key,
    @ObjectModel.readOnly: true
    parent_key,
    @EndUserText.label: 'Item No.'    
    @ObjectModel.readOnly: true
    so_item_pos,
    @ObjectModel.mandatory: true
    product_id,
    currency_code,
    @ObjectModel.readOnly: true
    gross_amount,
    @ObjectModel.readOnly: true
    net_amount,
    @ObjectModel.readOnly: true
    tax_amount,
    @ObjectModel.mandatory: true
    unit,
    @ObjectModel.mandatory: true
    quantity,
    
    @ObjectModel.association.type: [#TO_COMPOSITION_PARENT, #TO_COMPOSITION_ROOT]
    _SalesOrder
}
```

##Consumption View 
###Sales Order Header (Z_C_SO)

```abap
@AbapCatalog.sqlViewName: 'ZCSOMOB67'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Header'

@Metadata.allowExtensions: true
@OData.publish: true
@Search.searchable: true

@ObjectModel: {
    compositionRoot: true,
    transactionalProcessingDelegated: true,
    semanticKey: ['SoId'],
    representativeKey: 'SoId',
    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true,
    draftEnabled: true
}
define view Z_C_SO_MOB67
  as select from Z_I_SO_MOB67
  association [0..*] to Z_C_SOI_MOB67 as _Item     on $projection.UUID = _Item.ParentUUID
  association [0..1] to I_Currency    as _Currency on $projection.Currency = _Currency.Currency
  association [0..1] to Z_C_BPA_MOB67 as _Customer on $projection.BpId = _Customer.BpId
  association [0..*] to Z_I_StatusVH  as _StatusVH on $projection.DeliveryStatus = _StatusVH.DomainKey
{
      //Z_I_SO_MOB67
  key node_key         as UUID,
      @Search.defaultSearchElement: true
      so_id            as SoId,
      created_by       as CreatedBy,
      created_at       as CreatedAt,
      changed_by       as ChangedBy,
      changed_at       as ChangedAt,
      @Consumption.valueHelp: '_Customer'
      bp_id            as BpId,
      @Semantics.currencyCode: true
      @Consumption.valueHelp: '_Currency'
      currency_code    as Currency,
      @Semantics.amount.currencyCode: 'Currency'
      gross_amount     as GrossAmount,
      @Semantics.amount.currencyCode: 'Currency'
      net_amount       as NetAmount,
      @Semantics.amount.currencyCode: 'Currency'
      tax_amount       as TaxAmount,
      billing_status   as BillingStatus,
      @Consumption.valueHelp: '_StatusVH'
      delivery_status  as DeliveryStatus,
      overall_status   as OverallStatus,
      buy_contact_guid as ContactUUID,
      payment_method   as PaymentMethod,
      payment_terms    as PaymentTerm,

      /* Associations */
      @ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
      _Item,
      _Currency,
      _Customer,
      _StatusVH
}
```

###Sales Order Item (Z_C_SOI)

```abap
@AbapCatalog.sqlViewName: 'ZCSOIMOB67'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Item'
@Metadata.allowExtensions: true

@ObjectModel: {
    semanticKey: ['ItemPos'],

    createEnabled: true,
    updateEnabled: true,
    deleteEnabled: true
}
define view Z_C_SOI_MOB67
  as select from z_i_soi_mob67
  association [1..1] to Z_C_SO_MOB67    as _SalesOrder on $projection.ParentUUID = _SalesOrder.UUID
  association [0..1] to I_Currency      as _Currency   on $projection.Currency = _Currency.Currency
  association [0..1] to I_UnitOfMeasure as _Unit       on $projection.Unit = _Unit.UnitOfMeasure
  association [0..*] to snwd_pd         as _Product    on $projection.Product = _Product.product_id
{
  key node_key      as ItemUUID,
      parent_key    as ParentUUID,
      so_item_pos   as ItemPos,
      @Consumption.valueHelp: '_Product'
      product_id    as Product,
      @Consumption.valueHelp: '_Currency'
      @Semantics.currencyCode: true
      currency_code as Currency,
      @Semantics.amount.currencyCode: 'Currency'
      gross_amount  as GrossAmount,
      @Semantics.amount.currencyCode: 'Currency'
      net_amount    as NetAmount,
      @Semantics.amount.currencyCode: 'Currency'
      tax_amount    as TaxAmount,
      @Consumption.valueHelp: '_Unit'
      @Semantics.unitOfMeasure: true
      unit          as Unit,
      @Semantics.quantity.unitOfMeasure: 'Unit'
      quantity      as Quantity,

      /* Associations */
      @ObjectModel.association.type: [#TO_COMPOSITION_PARENT, #TO_COMPOSITION_ROOT]
      _SalesOrder,
      _Currency,
      _Product,
      _Unit
}
```

##Metadata Extension
###Sales Order Header (Z_C_SO)

```abap
@Metadata.layer: #CORE

@UI: { 
    headerInfo: { 
        typeName: 'Sales Order', 
        typeNamePlural: 'Sales Orders',
        title: { value: 'SoId' },
        description: { value: '_Customer.company_name' }
    }
}
annotate view Z_C_SO_MOB67
    with     
{
    @UI: { 
        facet: [
            { 
                id: 'HeaderFacet',
                type: #COLLECTION,
                purpose: #STANDARD,
                label: 'Order Information'            
            },
            // Header Info Group
            { 
                id: 'BasicGroup',
                parentId: 'HeaderFacet',
                type: #FIELDGROUP_REFERENCE,
                purpose: #STANDARD,
                label: 'Basic Information',
                targetQualifier: 'BasicFieldGroup'            
            },
            // Status Group
            { 
                id: 'StatusGroup',
                type: #FIELDGROUP_REFERENCE,
                parentId: 'HeaderFacet',
                purpose: #STANDARD,
                label: 'Order Status',
                targetQualifier: 'StatusFieldGroup'               
            },
            // Creation date time
            { 
                id: 'DateTimeGroup',
                type: #FIELDGROUP_REFERENCE,
                parentId: 'HeaderFacet',
                purpose: #STANDARD,
                label: 'Create/Update Datetime',
                targetQualifier: 'DateTimeFieldGroup'              
            },
            //Items
            { 
                id: 'ItemFacet',
                type: #LINEITEM_REFERENCE,
                purpose: #STANDARD,
                label: 'Item',
                targetElement: '_Item'           
            }            
        ]
        
    }


    @UI.hidden: true
    UUID;
    
    @UI: { 
        selectionField: [{position: 10 }],
        lineItem: [
            { position: 10, importance: #HIGH },
            { type: #FOR_ACTION, position: 10, dataAction: 'BOPF:SET_TO_DELIVERED', label: 'Set to Delivered' }
        ]
    }
    SoId;

    @UI: { 
        selectionField: [{position: 20 }],
        lineItem: [{ position: 20, importance: #HIGH }],
        fieldGroup: [{ qualifier: 'DateTimeFieldGroup', position: 10, importance: #MEDIUM }]
    }
    CreatedAt;
        
    @UI: { 
        lineItem: [{ position: 30, importance: #HIGH }],
        fieldGroup: [{ qualifier: 'DateTimeFieldGroup', position: 20, importance: #MEDIUM }]
    }
    CreatedBy;
    
    @UI: { 
        fieldGroup: [{ qualifier: 'DateTimeFieldGroup', position: 30, importance: #MEDIUM }]
    }
    ChangedAt;

    @UI: { 
        fieldGroup: [{ qualifier: 'DateTimeFieldGroup', position: 40, importance: #MEDIUM }]
    }    
    ChangedBy;
    
    @UI: { 
        selectionField: [{position: 30 }],
        lineItem: [
            { position: 40, importance: #HIGH, type: #WITH_INTENT_BASED_NAVIGATION, semanticObjectAction: 'manage' }
        ],
        fieldGroup: [{ qualifier: 'BasicFieldGroup', position: 10, importance: #HIGH }]
    }
    @Consumption.semanticObject: 'BusinessPartner'
    BpId;      
    
    @UI: { 
        selectionField: [{ position: 40 }],
        fieldGroup: [{ qualifier: 'BasicFieldGroup', position: 20, importance: #HIGH }]
    }
    Currency;
    
    @UI: { 
        lineItem: [{ position: 50, importance: #MEDIUM }],
        fieldGroup: [{ qualifier: 'BasicFieldGroup', position: 30, importance: #MEDIUM }]
    }    
    GrossAmount;

    @UI: { 
        selectionField: [{ position: 50 }],
        lineItem: [{ position: 60, importance: #MEDIUM }],
        fieldGroup: [{ qualifier: 'StatusFieldGroup', position: 10, importance: #MEDIUM }]
    }
    DeliveryStatus;
    
    @UI: { 
        lineItem: [{ position: 70, importance: #MEDIUM }],
        fieldGroup: [{ qualifier: 'StatusFieldGroup', position: 20, importance: #MEDIUM }]
    }
    OverallStatus;
    
}




    @UI: { 
        selectionField: [{position: 10 }]
    }
    SoId;

    @UI: { 
        selectionField: [{position: 20 }]
    }
    CreatedAt;
        
    @UI: { 
        selectionField: [{position: 30 }]
    }
    BpId;      
    
    @UI: { 
        selectionField: [{ position: 40 }]
    }
    Currency;

    @UI: { 
        selectionField: [{ position: 50 }]
    }
    DeliveryStatus;
```

###Sales Order Item (Z_C_SOI)

```abap
@Metadata.layer: #CORE

@UI: { 
    headerInfo: { 
        typeName: 'Item', 
        typeNamePlural: 'Items',
        title: { value: 'ItemPos' },
        description: { value: 'Product' }
    }
}
annotate view Z_C_SOI_MOB67
    with 
{
    @UI: { 
        facet: [
            { 
                id: 'ItemFacet',
                type: #COLLECTION,
                purpose: #STANDARD,
                label: 'Item Information'            
            },
            // Header Info Group
            { 
                id: 'BasicGroup',
                parentId: 'ItemFacet',
                type: #FIELDGROUP_REFERENCE,
                purpose: #STANDARD,
                label: 'Basic Information',
                targetQualifier: 'BasicFieldGroup'            
            },
            // Amount Group
            { 
                id: 'PriceGroup',
                type: #FIELDGROUP_REFERENCE,
                parentId: 'ItemFacet',
                purpose: #STANDARD,
                label: 'Price Information',
                targetQualifier: 'PriceFieldGroup'               
            }           
        ]
        
    }

    //Z_C_SOI_MOB67
    @UI.hidden: true
    ItemUUID;
    @UI.hidden: true
    ParentUUID;
    
    @UI: { 
        lineItem: [{ position: 10, importance: #HIGH }]
    }
    ItemPos;
    
    @UI: { 
        lineItem: [{ position: 20, importance: #HIGH }],
        fieldGroup: [{ qualifier: 'BasicFieldGroup', position: 10, importance: #HIGH  }]
    }    
    Product;
    
    @UI: { 
        lineItem: [{ position: 30, importance: #HIGH }],
        fieldGroup: [{ qualifier: 'BasicFieldGroup', position: 20, importance: #HIGH  }]
    }     
    Quantity;
    @UI: { 
        fieldGroup: [{ qualifier: 'BasicFieldGroup', position: 30, importance: #HIGH  }]
    }     
    
    Currency;    
    
    @UI: { 
        lineItem: [{ position: 40, importance: #HIGH }],
        fieldGroup: [{ qualifier: 'PriceFieldGroup', position: 10, importance: #HIGH  }]
    }     
    GrossAmount;
    
    @UI: { 
        lineItem: [{ position: 50, importance: #MEDIUM }],
        fieldGroup: [{ qualifier: 'PriceFieldGroup', position: 20, importance: #MEDIUM  }]
    }     
    NetAmount;
    
    @UI: { 
        lineItem: [{ position: 60, importance: #MEDIUM }],
        fieldGroup: [{ qualifier: 'PriceFieldGroup', position: 30, importance: #MEDIUM  }]
    }     
    TaxAmount;
     
}
```

##検索ヘルプ用ビュー
###Business Partner検索用
####Interface View (Z_I_BPA)

```abap
@AbapCatalog.sqlViewName: 'ZIBPAMOB67'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Supplier Basic View'
@VDM.viewType: #BASIC
define view Z_I_BPA_MOB67
  as select from snwd_bpa
{
//  key node_key      as UUID,
      key bp_id         as BpId,
      bp_role       as BpRole,
      email_address as Email,
      phone_number  as Phone,
      fax_number    as Fax,
      web_address   as Web,
      company_name  as CompanyName,
      currency_code as Currency
}
```

####Consumption View (Z_C_BPA)

```abap
@AbapCatalog.sqlViewName: 'ZCBPAMOB67'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Supplier Consumption View'
@VDM.viewType: #CONSUMPTION
@ObjectModel.semanticKey: ['BpId']
@Metadata.allowExtensions: true
define view Z_C_BPA_MOB67 as select from Z_I_BPA_MOB67 {
    //z_i_bpa_mob67
    //key UUID,
    key BpId,
    BpRole,
    Email,
    Phone,
    Fax,
    Web,
    CompanyName,
    Currency
}
```

####Metadata Extension (Z_C_BPA)
※ナビゲーション先のアプリを作るためにUIアノテーションを設定

```abap
@Metadata.layer: #CORE
@UI: { 
    headerInfo: { 
        typeName: 'Business Partner',
        typeNamePlural: 'Business Partners',
        title: { value: 'BpId' },
        description: { value: 'CompanyName' }        
    }
}
annotate view Z_C_BPA_MOB67
    with 
{
//    @UI.hidden: true
//    UUID;
    
    @UI: { 
        selectionField: [{ position: 10 }],
        lineItem: [{ position: 10, importance: #HIGH }],
        identification: [{ position: 10 }]
    }    
    BpId;
    
    @UI: { 
        selectionField: [{ position: 20 }],
        lineItem: [{ position: 20, importance: #HIGH }],
        identification: [{ position: 20 }]
    }     
    CompanyName;
    
    @UI: { 
        selectionField: [{ position: 30 }],
        lineItem: [{ position: 30, importance: #MEDIUM }],
        identification: [{ position: 30 }]
    }
    BpRole;    
    
    @UI: { 
        lineItem: [{ position: 40, importance: #MEDIUM }],
        identification: [{ position: 40 }]        
    }     
    Currency;

    @UI: { 
        identification: [{ position: 50 }]        
    }  
    Email;
    
    @UI: { 
        identification: [{ position: 60 }]        
    }      
    Phone;
    
    @UI: { 
        identification: [{ position: 70 }]        
    }      
    Fax;
    
    @UI: { 
        identification: [{ position: 80 }]        
    }      
    Web;
    
}
```

###Status検索用
ドメインテキストをベースにした検索ヘルプ

####Interface View (Z_I_Domain_Text)

```abap
@AbapCatalog.sqlViewName: 'ZIDOMAINTX'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Domain Text'

@ObjectModel.dataCategory: #TEXT
@Search.searchable: true
define view Z_I_Domain_Text
  as select from dd07t
{
      //dd07t
  key domname    as DomainName,
      @Semantics.language: true
  key ddlanguage as Language,
  key domvalue_l as DomainKey,
      @Semantics.text: true
      @Search: { defaultSearchElement: true, fuzzinessThreshold: 0.8 }
      ddtext     as Text
      
}where as4local = 'A'
```

####Interface View (Z_I_StatusVH)

```abap
@AbapCatalog.sqlViewName: 'ZISTATUSVH'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Value Help for Status'

@Search.searchable: true
@ObjectModel.representativeKey: 'DomainKey'
@ObjectModel.resultSet.sizeCategory: #XS
define view Z_I_StatusVH as select from dd07l as Domain 
association [0..*] to Z_I_Domain_Text as _Text
on  $projection.DomainName = _Text.DomainName
and $projection.DomainKey = _Text.DomainKey 
{
    @UI.hidden: true
    key Domain.domname as DomainName,
    @ObjectModel.text.association: '_Text'
    @Search.defaultSearchElement: true    
    key Domain.domvalue_l   as DomainKey,
    _Text    
}
where Domain.as4local = 'A'
and Domain.domname = 'D_SO_OR'
```

##BOPF
###Actions
####DeliveryStatusを設定

```abap
CLASS zcl__a_set_to_delivered DEFINITION
  PUBLIC
  INHERITING FROM /bobf/cl_lib_a_supercl_simple
  FINAL
  CREATE PUBLIC .

PUBLIC SECTION.

  METHODS /bobf/if_frw_action~execute
    REDEFINITION .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS zcl__a_set_to_delivered IMPLEMENTATION.


  METHOD /bobf/if_frw_action~execute.
    DATA: lt_so TYPE ztiso_mob67.

    io_read->retrieve(
      EXPORTING
        iv_node                 = is_ctx-node_key
        it_key                  = it_key
      IMPORTING
        et_data                 = lt_so
    ).

    LOOP AT lt_so REFERENCE INTO DATA(lr_so).

      lr_so->delivery_status = 'D'.
      io_modify->update(
        EXPORTING
          iv_node           = is_ctx-node_key
          iv_key            = lr_so->key
          iv_root_key       = lr_so->root_key
          is_data           = lr_so
          it_changed_fields = VALUE #(
            ( zif_i_so_mob67_c=>sc_node_attribute-z_i_so_mob67-delivery_status )
          )
      ).
    ENDLOOP.

  ENDMETHOD.
ENDCLASS.
```

###Determinations

####SoIdの採番

```abap
CLASS zcl__d_set_so_id DEFINITION
  PUBLIC
  INHERITING FROM /bobf/cl_lib_d_supercl_simple
  FINAL
  CREATE PUBLIC .

PUBLIC SECTION.

  METHODS /bobf/if_frw_determination~execute
    REDEFINITION .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS zcl__d_set_so_id IMPLEMENTATION.


  METHOD /bobf/if_frw_determination~execute.
    DATA: lt_so TYPE ztiso_mob67,
          lv_so_max TYPE snwd_so_id.

    io_read->retrieve(
      EXPORTING
        iv_node                 = is_ctx-node_key
        it_key                  = it_key
      IMPORTING
        et_data                 = lt_so
    ).

    "Get max so
    SELECT MAX( so_id ) FROM zso_mob67
    INTO @lv_so_max.

    IF lv_so_max IS INITIAL.
      lv_so_max = 500000000.
    ENDIF.

    LOOP AT lt_so REFERENCE INTO DATA(lr_so).

      lv_so_max += 1.
      CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
        EXPORTING
          input  = lv_so_max
        IMPORTING
          output = lr_so->so_id.

      io_modify->update(
        EXPORTING
          iv_node           = is_ctx-node_key
          iv_key            = lr_so->key
          iv_root_key       = lr_so->root_key
          is_data           = lr_so
          it_changed_fields = VALUE #(
            ( zif_i_so_mob67_c=>sc_node_attribute-z_i_so_mob67-so_id )
          )
      ).
    ENDLOOP.

  ENDMETHOD.
ENDCLASS.
```
####タイムスタンプの設定

```abap
CLASS zcl__d_set_timestamp DEFINITION
  PUBLIC
  INHERITING FROM /bobf/cl_lib_d_supercl_simple
  FINAL
  CREATE PUBLIC .

PUBLIC SECTION.

  METHODS /bobf/if_frw_determination~execute
    REDEFINITION .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS zcl__d_set_timestamp IMPLEMENTATION.


  METHOD /bobf/if_frw_determination~execute.
    DATA: lt_so TYPE ztiso_mob67.

    io_read->retrieve(
      EXPORTING
        iv_node                 = is_ctx-node_key
        it_key                  = it_key
      IMPORTING
        et_data                 = lt_so
    ).

    LOOP AT lt_so REFERENCE INTO DATA(lr_so).

     IF lr_so->created_at IS INITIAL.
       GET TIME STAMP FIELD lr_so->created_at.
       lr_so->created_by = sy-uname.
     ENDIF.
     GET TIME STAMP FIELD lr_so->changed_at.
     lr_so->changed_by = sy-uname.

      io_modify->update(
        EXPORTING
          iv_node           = is_ctx-node_key
          iv_key            = lr_so->key
          iv_root_key       = lr_so->root_key
          is_data           = lr_so
          it_changed_fields = VALUE #(
            ( zif_i_so_mob67_c=>sc_node_attribute-z_i_so_mob67-created_at )
            ( zif_i_so_mob67_c=>sc_node_attribute-z_i_so_mob67-created_by )
            ( zif_i_so_mob67_c=>sc_node_attribute-z_i_so_mob67-changed_at )
            ( zif_i_so_mob67_c=>sc_node_attribute-z_i_so_mob67-changed_by )
          )
      ).
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.
```