migrate-segw-to-rap について
従来の SEGW OData V2 サービスを解析し、同じ SAP システム上に RAP サービスとして再構築するワークフロースキルです。旧サービスを稼働させたまま、新しいオブジェクトを別パッケージに配置します。
主な機能
- 01SEGW モデルとプロバイダークラスの調査
- 02エンティティと関連の RAP への変換
- 03旧操作の RAP ビヘイビアへの対応付け
- 04CDS ビューとサービスオブジェクトの作成
活用シーン
- 01旧 OData V2 バックエンドの RAP への移行
- 02新旧サービスの並行稼働
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ arc-mcp/ arc-1 --skill migrate-segw-to-rapインストール後、Claude Code で/migrate-segw-to-rapを入力して読み込まれたか確認します。
詳細
SEGW OData V2 サービスを RAP サービスに移行する
従来の SEGW で構築された OData V2 サービス(MPC/DPC/MPC_EXT/DPC_EXT)をリバースエンジニアリングし、同じ SAP システム上の最新の RAP サービス(CDS ルート+プロジェクション+BDEF+SRVD+SRVB+ビヘイビアプール)に変換します。 並行して稼働します。レガシーサービスは引き続き稼働し、新しい RAP サービスは個別のリセット可能な パッケージに作成されます。
ドメインの例。 このスキルのテンプレートでは、例として
Project → Tasks → TimeEntriesドメインを使用しています(ZR_DM_PROJECT、ZBP_DM_PROJECT、ZDM_PROJECT_Dのようなエンティティ名)。構成を 具体的に示すためのものです。ソースサービスに合わせて、ユーザーのエンティティに置き換えてください。このスキルを実行する LLM は、 すべてのエンティティ識別子をソースサービスに一致するように書き換えてください。
スマートなデフォルト設定(調査中に適用します。事前に質問しないでください)
| 設定 | デフォルト | 理由 |
|---|---|---|
| 検出戦略 | MPC クラスのソース(Tier 1) | ARC-1 がネイティブに読み取れ、$metadata より情報が豊富です |
| 対象パッケージ | ユーザー指定。指定がない場合は、SAPManage(action="create_package") を使ってソースパッケージの子パッケージを作成します。デフォルト名は <source_package>_RAP です。ユーザーから明示的な依頼がない限り、$TMP にはフォールバックしません。 | 移行の出力を分離し、リセットできる状態に保ちます。 |
| 対象トランスポート | ユーザーが指定した既存のもの、または SAPTransport(action="create", description="RAP migration of <legacy_service>", package="<target_package>") で自動作成します。 | $TMP 以外のパッケージでは必須です。 |
| 対象の OData バージョン | V4 | 現行の SAP 標準であり、FE 対応です |
| RAP シナリオ | 内部採番によるマネージド | 読み取り中心のシナリオとして最もシンプルで、従来の SEGW の動作に合致します |
| ドラフト | 読み取り中心のエンティティではオフにします。従来のサービスが CUD(ファンクションインポート、ディープインサート、またはエンティティセットの sap:updatable=true)を公開しており、ユーザーが Fiori Elements との互換性を望む場合はオンにします。 | 従来のサービスの動作契約に合わせます。FE のリストと OP はドラフトをオンにすると最適に機能します。 |
| Strict モード | strict ( 2 ) | 現行のベストプラクティスです |
| プロジェクション層 | 必須 — すべての ZR_* ルートビューに対して、必ず ZC_* プロジェクションを作成します。サービスバインディングではルートではなく、プロジェクションを公開します。 | スキルの実行 1 では CDS エラーの誤読によりプロジェクションが省略されました。LLM が決して省略しないよう、明記しています。 |
プロジェクション上の provider contract | ルートプロジェクションにのみ設定し、ルートビューにも子プロジェクションにも設定しません。コントラクトは正確に 1 層に設定します。7.58 でプロジェクション BDEF と use draft を組み合わせる場合は transactional_query を使用します。ドラフトなしの読み取り中心のシナリオでは transactional_interface を使用します。 | ルートビューに設定すると → 「only valid on projection views」。子プロジェクションに設定すると → 「inappropriate provider contract on …」。子は redirected to parent 経由で公開され、ルートプロジェクションから BO コントラクトを継承します。実行 3 では、7.58 でドラフトを使うには _query が必要であることが確認されました。 |
| CDS のコンポジション構文(マネージド) | composition [0..*] of <child> as <name> — on 句は指定しません。キーの関連付けは、子の対応するキー項目/with foreign key によって暗黙的に行われます | 7.58 では、マネージドコンポジションで on が拒否されます。修正するには on を削除します。association to への切り替えではありません。 |
7.58 の @Semantics.* | createdAt、lastChangedAt、および localInstanceLastChangedAt は、いずれも 7.58 で有効です。SAP 独自の RAP ジェネレーターと同様に、localInstanceLastChangedAt はローカルインスタンス ETag 項目(abp_locinst_lastchange_tstmpl)に設定します。@Semantics.businessDate.from/to も使用できます。 | S/4HANA 2023(758、2026-06-12)で実機検証済みです。localInstanceLastChangedAt を使ったスタンドアロン CDS ビューと、businessDate.from/to を使ったスタンドアロン CDS ビューは、どちらも有効化されました。以前の「7.58 では未知のアノテーション」というメモは誤診でした。ドラフトアノテーションが実際に存在しないのは、7.55 より前のリリースのみです。 |
| ドラフトテーブルの項目名(draft=ON の場合) | アンダースコアを含まない BO エイリアスの大文字小文字表記を使用します。例:projectid、startdate。project_id、start_date は使用しません。ABAP は BDEF エイリアス(ProjectId、StartDate)を PROJECTID、STARTDATE に正規化します。ドラフトテーブルの参照では、アクティブテーブルの snake_case ではなく、この正規化された名前が使われます。 | 実行 2 では、BDEF の有効化が 「key field PROJECTID expected at position 2, found PROJECT_ID」 というエラーで失敗しました。アクティブテーブルでは snake_case を維持できます(BDEF マッピングで処理されます)。ドラフトテーブルにはマッピング句がないため、維持できません。 |
| 命名 | SAP 標準の Z<prefix>_<entity>:ZR_ ルート、ZC_ プロジェクション、ZI_<entity>_BEH BDEF、ZBP_ ビヘイビアプール、ZUI_<service>_O4 V4 SRVB | SAP 内部の規約に沿っており、先頭の Z は顧客名前空間です |
| 書き込み前の lint | オン | ARC-1 の設定で SAP_ABAP_RELEASE=<your_release> を指定し(PR #255)、lint プリセットをシステムのリリースに合わせます。以前の SAP_LINT_BEFORE_WRITE=false 回避策は不要になりました。 |
| 書き込み前の SAP チェック | 有効化を妨げる問題のみオン | --check-before-write ではなく、有効化のフィードバックを利用します |
入力
ユーザーは最低限、次の情報を指定する必要があります。
- 次のいずれか 1 つ:SEGW サービスの技術名(例:
Z<SOMETHING>_SRV)、SEGW で生成されたクラスを含むパッケージ、または MPC クラス名を直接指定します。指定がない場合は確認してください。 - 新しい RAP オブジェクトの対象パッケージ。指定がなく、ソースパッケージが判明している場合、スキルは
<source_package>_RAPを作成します。それ以外の場合は確認します。ユーザーが明示的に要求した場合に限り、$TMPをデフォルトにします。 $TMP以外への書き込みに必要なトランスポート(既存または自動作成。スマートデフォルトを参照)。
任意:
- RAP シナリオ(デフォルトは内部採番を使用するマネージドシナリオ)。
- ドラフトのオン/オフ(スキルが従来の CUD サーフェスから推測します)。
- OData バージョン(デフォルトは V4)。
- 命名規則の上書き(デフォルトは SAP 標準の
Z<prefix>_<entity>)。
従来の識別子だけが指定された場合、スキルはスマートデフォルトを適用し、書き込みを行う前に、フェーズ 5 で解決済みの計画を提示してユーザーの ok を求めます。
フェーズ 0 — 認可の事前確認
モデリング作業を始める前に実行します。必要な情報が不足している場合は、ユーザーに必要な SAP 認可オブジェクトを具体的に示して、正常に中断します。
0a. サーバー側の機能および認可の確認
SAPManage(action="probe")
応答から次の内容を確認します。
systemType∈ {onprem,btp} — 命名規則と管理フィールドの選択に必要ですrap.available == true— 次のメッセージで失敗します:"このシステムでは RAP/CDS エンドポイントが公開されていません。移行を続行できません。"transport.available == true— 次のメッセージで失敗します:"トランスポート API を利用できません。ICF/sap/bc/adt/cts/transportsを確認してください。"authProbe.searchAccess == true— 次のメッセージで失敗します:"ユーザーには ADT オブジェクトの検索権限がありません。S_DEVELOPACTVT=03 OBJTYPE=CLAS DEVCLASS=<source-pkg>が必要です。"authProbe.transportAccess == true— 次のメッセージで失敗します:"ユーザーにはトランスポートの読み取り権限がありません。S_TRANSPRTACTVT=03 が必要です。"
0b. 従来の MPC クラスを読み取る — ソースの読み取り権限を確認します
SAPRead(type="CLAS", name="<MPC>", method="*")
HTTP 403 の場合は、次のメッセージを表示して停止します:"<MPC> を読み取れません。S_DEVELOP ACTVT=03 OBJTYPE=CLAS
DEVCLASS=<source-pkg> P_GROUP=<auth-group> が必要です。不足している項目を確認するには、SAP GUI で SU53 を実行してください。"
0c. 対象パッケージへの書き込み権限を確認する
SAPTransport(action="check", objectType="DDLS", objectName="ZX_PRECHECK", package="<target_package>")
続けて、使い捨ての DDLS に対して書き込みと削除のドライランを実行します。
SAPWrite(action="create", type="DDLS", name="ZR_DM_PRECHECK",
source="@AccessControl.authorizationCheck: #NOT_REQUIRED\ndefine root view entity ZR_DM_PRECHECK as select from t000 { client }",
description="ARC-1 preflight",
package="<target_package>",
transport="<transport>")
SAPWrite(action="delete", type="DDLS", name="ZR_DM_PRECHECK")
作成時に 403 が返された場合は、次のメッセージを表示して停止します:"<target_package> に書き込めません。S_DEVELOP ACTVT=01,02,07
OBJTYPE=DDLS DEVCLASS=<target_package> が必要です。"
3 つすべてに成功した場合は、"フェーズ 0 ✓ 認可 OK — 検出に進みます。" と表示して続行します。
フェーズ 1 — 従来のサービスを検出する
1a. MPC クラス名を解決する
ユーザーがサービス名(例:<legacy_service>)を指定した場合:
- 規則を試します:
_SRVを取り除き、_MPCを追加して<MPC_class>にします。SAPReadで確認します。 - 見つからない場合:
ユーザーに選択を求めます。SAPQuery(action="sql", sql="SELECT obj_name, devclass FROM tadir WHERE pgmid = 'R3TR' AND object = 'CLAS' AND obj_name LIKE 'Z%_MPC' ORDER BY obj_name")
ユーザーがパッケージ(例:<source_package>)を指定した場合:
-
Filter for classes endingSAPRead(type="DEVC", name="<package>")_MPC.
If user gave an MPC class directly: use it as-is.
Determine the corresponding _MPC_EXT, _DPC, _DPC_EXT by name convention.
1b. Confirm the four classes exist + record metadata
SAPSearch(action="object", query="<base>_*", maxResults=10)
想定される結果:4 件。各項目の objectName と packageName を記録します。
ユーザーに次の内容を表示します。
Legacy service: <legacy_service>
Package: <source_package>
MPC class: <MPC_class> (model)
MPC_EXT: <MPC_EXT_class> (model overrides — usually empty)
DPC class: <DPC_class> (data provider — generated)
DPC_EXT: <DPC_EXT_class> (data provider — your custom code)
フェーズ 2 — MPC ソースから OData モデルを抽出する
MPC クラスのプライベートな DEFINE_* メソッドにモデル全体が含まれています。これらを読み取ります。
2a. メソッド一覧
SAPRead(type="CLAS", name="<MPC>", method="*")
次を探します。
DEFINE_<entity>— エンティティタイプごとに 1 つDEFINE_ASSOCIATIONS— すべての関連とナビゲーションプロパティDEFINE_ACTIONS— すべてのファンクションインポート
2b. エンティティごとの読み取り
各 DEFINE_<entity> メソッドに対して:
SAPRead(type="CLAS", name="<MPC>", method="DEFINE_<ENTITY>")
本体を解析して、次の項目を確認します。
- エンティティタイプ名とエンティティセット名(
create_entity_type(...)とcreate_entity_set(...)を探します) - 基になる ABAP DDIC のバインド(
bind_structure(...)またはset_data_source(...)を探します) - 各プロパティ:名前、EDM タイプ、最大長、主キーかどうか、ソート/フィルター/作成/更新が可能かどうか(
create_property(...)に続くset_*呼び出しを探します)
2c. 関連
SAPRead(type="CLAS", name="<MPC>", method="DEFINE_ASSOCIATIONS")
各 create_association(...) について、次の項目を解析します。
- 関連名
- プリンシパルエンティティとカーディナリティ
- 従属エンティティとカーディナリティ
- ナビゲーションプロパティ名
- 参照制約(プリンシパルプロパティ ↔ 従属プロパティ)
2d. 関数インポート
SAPRead(type="CLAS", name="<MPC>", method="DEFINE_ACTIONS")
各 create_action(...) を解析します。
- アクション/関数インポート名
- HTTP メソッド
- 戻り値の型(エンティティ/複合型/プリミティブ型/void)
- 戻り値の多重度
- パラメーター:名前、型、長さ、モード(in/out/inout)
2e. 構造化モデルにまとめる
フェーズ 2 の完了後、次のような完全な表を出力できるようになります。
=== Extracted OData model ===
Entity types (3):
Project bound to ZDM_PROJECT key: ProjectId 12 properties
Task bound to ZDM_TASK key: TaskId 15 properties (FK ProjectId)
TimeEntry bound to ZDM_TIMEENTRY key: EntryId 15 properties (FKs TaskId, ProjectId)
Entity sets (3): ProjectSet, TaskSet, TimeEntrySet
Associations (2):
Project_Tasks Project [1] ↔ Task [0..n] ref: ProjectId ↔ ProjectId nav on Project: Tasks
Task_TimeEntries Task [1] ↔ TimeEntry [0..n] ref: TaskId ↔ TaskId nav on Task: TimeEntries
Function imports (1):
ApproveProject POST in: ProjectId (Edm.String, len 10) return: Project (Entity, 1)
これを出力し、先に進む前にユーザーに 「想定どおりですか?」 と尋ねます。
フェーズ 3 — DPC_EXT から動作を抽出する
3a. メソッド一覧
SAPRead(type="CLAS", name="<DPC_EXT>", method="*")
再定義された各メソッドを分類します。
| メソッドパターン | RAP での対応 |
|---|---|
<EntitySet>_GET_ENTITYSET | CDS ビューの読み取りアクセス。マネージドシナリオでは通常、追加作業は不要です |
<EntitySet>_GET_ENTITY | 同上。マネージドシナリオでは追加作業は不要です |
<EntitySet>_CREATE_ENTITY | BDEF で create を有効化(任意で判定/バリデーションを追加) |
<EntitySet>_UPDATE_ENTITY | BDEF で update を有効化 |
<EntitySet>_DELETE_ENTITY | BDEF で delete を有効化 |
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~EXECUTE_ACTION | 関数インポートに応じて、BDEF の静的またはインスタンス action(...) に対応 |
3b. メソッド本体の読み取り(ロジックがあるもののみ)
再定義された各メソッドについて、次のように判断します。
- 単純な GET(
SELECT * FROM <table> WHERE <key>とMOVE-CORRESPONDINGのみ):完全に削除します — RAP のマネージドシナリオで追加作業なしに処理されます。 - フィルター付き GET(追加の WHERE 条件、派生フィールド):CDS ビューのフィルターまたは フィールドエイリアスに変換します。
- 関数インポート:本体を読み、ビジネス上の効果(ステータス更新、計算、 外部呼び出し)を特定して、適切なビヘイビアプールメソッドを持つ BDEF アクションに変換します。
SAPRead(type="CLAS", name="<DPC_EXT>", method="<METHOD_NAME>")
ほとんどが単純なメソッドであれば、読み取りは 30 行程度です。複雑なビジネスロジックの場合は、100 行以上になることを想定し、慎重に書き換える計画を立ててください。
3c. ビヘイビアの概要にまとめる
=== Extracted behavior ===
Read-only (drop, free in RAP managed):
PROJECTSET_GET_ENTITYSET, PROJECTSET_GET_ENTITY,
TASKSET_GET_ENTITYSET, TASKSET_GET_ENTITY,
TIMEENTRYSET_GET_ENTITYSET, TIMEENTRYSET_GET_ENTITY
Function imports → BDEF actions:
ApproveProject(ProjectId) ➜ static action ApproveProject parameter $self
sets Status='A' + admin fields, returns Project
(legacy did UPDATE + COMMIT WORK + reread; RAP managed
handles persistence — no manual COMMIT needed)
Custom create/update/delete: none (read-only service)
_CREATE_ENTITY/_UPDATE_ENTITY/_DELETE_ENTITY が含まれていた場合は、それらを一覧にし、BDEF の判定/バリデーションにどのように変換されるかについて注記します。
フェーズ 4 — 基盤となるテーブルを読み取る
フェーズ 2 で特定した各テーブル(例:ZDM_PROJECT、ZDM_TASK、ZDM_TIMEENTRY)について、次のようにします。
SAPRead(type="TABL", name="<table>")
次の項目を記録します。
- ABAP 型と長さを含むフィールド一覧
- キーフィールド
- 管理フィールドの規約(従来型の
ERNAM/ERDAT/ERZET/AENAM/AEDAT/AEZETとモダンなabp_*のどちらか) - 外部キー参照(
with foreign key句)
テーブルはそのまま残し、新しい RAP CDS ビューから読み取ります。
フェーズ 5 — 設計計画とユーザーの承認
書き込み操作を行う前に、RAP 設計全体を設計計画として提示し、ユーザーの明示的な承認を得てから進めます。
5a. 命名
Legacy → New RAP
ZDM_PROJECT (table, untouched) ─► ZR_DM_PROJECT (CDS root view entity)
ZC_DM_PROJECT (CDS projection view, exposed via service)
ZI_DM_PROJECT (BDEF for root)
ZDM_TASK (table, untouched) ─► ZR_DM_TASK + ZC_DM_TASK
ZDM_TIMEENTRY (table, untouched) ─► ZR_DM_TIMEENTRY + ZC_DM_TIMEENTRY
(no SEGW project on the new side) ─► ZUI_DM_PROJECTS (service definition — exposes ZC_*)
ZUI_DM_PROJECTS_O4 (service binding, OData V4 UI)
ZBP_DM_PROJECT (behavior pool class for actions)
5b. コンポジションツリー
root: ZR_DM_PROJECT
composition [0..*] of ZR_DM_TASK as _Tasks
composition [0..*] of ZR_DM_TIMEENTRY as _TimeEntries
SEGW のアソシエーションを 1 対 1 で対応付けます。ただし、ここではコンポジション(親が子を所有する関係)となり、RAP のマネージドライフサイクルセマンティクスが追加作業なしに利用できます。
5c. アクション
ZI_DM_PROJECT (BDEF):
define behavior for ZR_DM_PROJECT alias Project
persistent table zdm_project
lock master
authorization master ( instance )
etag master last_changed_at
{
field ( readonly ) ProjectId;
action approve_project result [1] $self; ← matches SEGW ApproveProject
}
approve_project アクション → ZBP_DM_PROJECT のハンドラー → フレームワークの MODIFY ENTITIES インターフェイスを介して status='A' と管理フィールドを設定します(手動の UPDATE + COMMIT は不要です)。
5d. サービスバインディング URL
Old (SEGW V2): /sap/opu/odata/sap/<legacy_service>
New (RAP V4): /sap/opu/odata4/sap/zui_dm_projects_o4/srvd_a2x/sap/zui_dm_projects/0001
(または、サービスバインディングが UI として公開される場合は、FLP にバインドされた URL。)
5e. 提示して確認する
計画全体をユーザーに出力します。明示的な 「はい、進めてください」 という返答、または変更依頼を待ちます。ユーザーが変更(別の命名、ドラフトの有効化、別の RAP シナリオなど)を希望する場合は、修正して再提示します。
非対話モード。 ユーザーの最初のプロンプトにフェーズ 5 相当の入力(レガシーサービス、ターゲットパッケージ、移送、シナリオ、ドラフトの有効/無効)がすでに含まれており、かつ「最初から最後まで実行」/「レビューで止まらない」と指示されている場合は、定型的な 「はい、進めますか?」 という確認を省略します。計画をマニフェストとして出力し、そのままフェーズ 5f/フェーズ 6 に進みます。フェーズ 5 で実際の競合(入力不足、矛盾するシナリオフラグなど)が見つかった場合のみ停止します。全工程の自動化が見せかけの確認で止まる問題については、Run 6 の調査結果を参照してください。
5f. 成果物リストを Phase 6 の契約として固定する
Phase 5 を終える前に、スキルは Phase 6 がステータスとともに返す成果物リストの契約を出力しなければなりません。以下をそのまま出力してください:
=== Phase 6 will create + activate these artifacts (Phase 5 → Phase 6 contract) ===
Roots (CDS): [ ] ZR_DM_PROJECT [ ] ZR_DM_TASK [ ] ZR_DM_TIMEENTRY
Projections (CDS): [ ] ZC_DM_PROJECT [ ] ZC_DM_TASK [ ] ZC_DM_TIMEENTRY
(projections are MANDATORY — service binding never exposes roots directly)
Draft tables (TABL): [ ] ZDM_PROJECT_D [ ] ZDM_TASK_D [ ] ZDM_TIMEENTRY_D
(only when draft is on — based on Phase 5d)
BDEFs: [ ] ZR_DM_PROJECT (root behavior) [ ] ZC_DM_PROJECT (projection behavior)
Behavior pool (CLAS): [ ] ZBP_DM_PROJECT (empty shell — generate_behavior_implementation will populate)
Service definition: [ ] ZUI_DM_PROJECTS (exposes projections, NOT roots)
Service binding: [ ] ZUI_DM_PROJECTS_O4 (OData V4 UI)
Manual steps remaining after Phase 6 build:
[ ] /IWFND/MAINT_SERVICE — register V4 service group ZUI_DM_PROJECTS_O4 (else 403 at runtime)
Phase 6 の最後にこのリストを順に確認し、各項目に ✓ または ✗ を付けた同じチェックリストを出力する必要があります(✗ の場合は失敗したツール呼び出しの引数と応答も記載します)。すべての項目が ✓ になるか、ユーザーが明示的に免除するまで、Phase 6 を完了と宣言しないでください。
Phase 6 — RAP スタックの構築
6a. 既存の成果物をリセットする(再実行しても安全)
<target_package> パッケージに前回の実行によるオブジェクトが含まれている場合は、まず削除します:
SAPRead(type="DEVC", name="<target_package>")
各オブジェクトについて、依存関係の逆順(SRVB → SRVD → BDEF → DDLS_C → DDLS → CLAS → TABL)で実行します:
SAPWrite(action="delete", type="<type>", name="<name>", transport="<transport>")
(SAP は再実行前にトランスポートをリリースすることを推奨していますが、リセット可能なデモでは <transport> を再利用すれば問題ありません。)
さらに TADIR とのクロスチェックを行います(Run 1 では、パッケージのスキャンで見つからなかったスタブドラフトテーブルが別のトランスポートに存在していました。Run 6 では、ADT の 404 と TADIR のゴースト行が共存する場合があることが判明しました。これは「スプリットブレイン」の障害モードです)。ARC-1 ≥ 0.9.5(PR #270)では、source="both" モードを使って ADT と DB TADIR を 1 回の呼び出しで照会し、名前が一致しない場合は splitBrain 警告配列を出力する方法が、リセット状態を判断するための正式な基準です:
SAPSearch(searchType="tadir_lookup",
source="both",
names=["ZDM_PROJECT_D","ZDM_TASK_D","ZDM_TIMEENTRY_D",
"ZR_DM_PROJECT","ZR_DM_TASK","ZR_DM_TIMEENTRY",
"ZC_DM_PROJECT","ZC_DM_TASK","ZC_DM_TIMEENTRY",
"ZI_DM_PROJECT_BEH","ZI_DM_TASK_BEH","ZI_DM_TIMEENTRY_BEH",
"ZBP_DM_PROJECT","ZUI_DM_PROJECTS","ZUI_DM_PROJECTS_O4"])
応答は次の順に確認します:
results— ADT 検出で見つかった名前(「オブジェクトが実際に存在するか」という確認)。空の場合、ADT の観点ではパッケージはクリーンです。splitBrain— ADT と DB の不一致がある名前(「ゴースト」のケース)。各配列項目には、どちらのソースが検出したかが示されています。実用上は、DB のみに存在する名前(TADIR 行はあるが ADT オブジェクトがない)はゴーストであり、SAPWrite createの処理ではすでに存在しないものとして扱います。ADT のみに存在する場合(まれです)は、ADT キャッシュが古い状態です。数秒待ってから、もう一度SAPSearchを実行してください。
source="both" には sql スコープが必要です(DB 側では Free SQL パスを使用します)。プロファイルにこのスコープがない場合は、ライブチェックに source="adt"(デフォルト)を使い、より広範なパッケージスキャンには SAPQuery を使用します:
SAPQuery(action="sql", sql="SELECT obj_name, object, devclass, korrnum FROM tadir
WHERE devclass = '<target_package>' AND obj_name LIKE 'Z%'")
計画している名前が別のパッケージにある場合、それは残存スタブです。続行する前に、実際のトランスポート経由で削除してください。
6a-gen.(任意)SAP 公式 ABAP ジェネレーターでルート BO を生成する
公式 SAP ABAP MCP サーバーが ARC-1 とともに接続されている場合(ABAP Development
Tools for VS Code に同梱。Eclipse ADT 3.60+ では有効化され、abap-mcp サーバーとして
表示されます)、その Generate ABAP Repository Objects フレームワークで、管理対象のドラフト付き
ルート BO を生成できます。
ただし、このスキルの代わりにはなりません。フェーズ 1~5(検出+変換)とフェーズ 6 の大部分は
引き続き ARC-1 の担当です。ジェネレーターは単一エンティティを一度だけ生成するためです。
「エンティティは最大 1 つで、コンポジションや階層はありません」、また 「生成後の使用を 想定していません。以降の変更には開発者による手動対応が必要です。」
そのため、SEGW 移行に適用できるのは、レガシーサービスが実質的に単一のルートエンティティで、
管理対象のドラフト付きであり、そのテーブルに最新のタイムスタンプ項目 last_changed : abp_lastchange_tstmpl
および local_last_changed : abp_locinst_lastchange_tstmpl がある場合に限られます。多くの SEGW サービスは、
従来の管理項目 ERDAT/AEDAT とファンクションインポートを含む複数エンティティです。**この場合、
ジェネレーターは適用できません。通常どおり手動で構築してください(6b)。**適用できる場合も、生成するのは
ルートのみです。ARC-1 がその後、子、コンポジション、アクション、項目マッピングを追加します
(ジェネレーターは一度しか生成できないため、これらには対応できません)。
使用する場合:
- 確認します。
abap_generators-list_generatorsツール(abap-mcpサーバー)がない場合は、6b に進み、手動で構築します。 - ID を解決します。リリースごとに異なるため、ハードコードしないでください。
abap_generators-list_generatorsを呼び出し、 表示名が "OData UI Service" のジェネレーターを探し、返されたidを使います(実環境で確認済み: SAP_BASIS 758 / S4 2023 ではuiservice、816 / ABAP Platform 2025 ではui-service。816 には "from scratch" のx-ui-serviceもあります)。一覧にない場合は 6b に進みます。 - スキーマから生成します。
abap_generators-get_schema(generatorId, packageName="<target_package>", referencedObjectType="TABL", referencedObjectName="<root table, e.g. ZDM_PROJECT>")を呼び出し、返された スキーマにあるすべての必須フィールドを入力してから、abap_generators-generate_objects(generatorId, …)を呼び出します。 これは変更操作です。<target_package>と<transport>を指定し、書き込み操作と同じガードレールに従ってください。 - ARC-1 に引き継ぎます。 6b のステップ 2 以降に進み、子/コンポジション、プロジェクションの契約レベル、
アクション(
approve_project)とハンドラー本体、有効化を行います。ルートにはジェネレーターが選んだ名前を適用してください。 このスキルのZR_*/ZC_*/ZI_*_BEHの計画に合わせて、ルート CDS/BDEF の命名を調整するか、 ジェネレーターの名前をそのまま採用して、フェーズ 5f の契約を更新してください。
実行の監査可能性を保つため、どちらの方法を選んだかを明記してください。迷った場合は、手動で構築する方法(6b)を優先してください。 複数エンティティから成る SEGW の構成で実証済みの方法であり、2 つ目の MCP サーバーにも依存しません。
6b. 構築順序 — 厳守し、独自の手順を加えない
以下の順序は、過去の失敗から得られたものです(Run 1、呼び出し #22~33)。**順序を変えないでください。**各ステップは、 次のステップで特定の失敗が起きるのを防ぎます。
ステップ 1 — ドラフトテーブル(フェーズ 5d.draft = ON の場合のみ)
SAPWrite(action="batch_create", objects=[
{ type: "TABL", name: "ZDM_PROJECT_D", source: "<draft table source — see template below>", package: "<target_package>", transport: "<transport>" },
{ type: "TABL", name: "ZDM_TASK_D", source: "<...>", package: "<target_package>", transport: "<transport>" },
{ type: "TABL", name: "ZDM_TIMEENTRY_D", source: "<...>", package: "<target_package>", transport: "<transport>" }
])
SAPActivate(action="activate", objects=[ {type:"TABL", name:"ZDM_PROJECT_D"}, ... ])
ドラフトテーブルのテンプレート — Run 2 で得られた 3 つのルール:
- 項目名には、アンダースコアを使わない BO エイリアスの大文字・小文字表記を使います(
projectid、startdate、project_id、start_dateではありません)。ABAP は BDEF エイリアスをアンダースコアなしの大文字に正規化します。 ドラフトのバインディングでは、稼働テーブルのスネークケース名ではなく、その名前で検索します。 - BDEF の
total etagとetag master句をバインドするには、creationtimestamp列とlastchangedstamp列が 必須です。 @AbapCatalog.enhancement.category : #NOT_EXTENSIBLEは必須です。これがないと、7.58 で TABL の保存に失敗します。
@EndUserText.label : 'Demo: Project (draft shadow)'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zdm_project_d {
" Field names match the BO aliases (no underscore) — required by RAP draft binding.
" The active table ZDM_PROJECT can keep its snake_case names; the BDEF mapping bridges
" them. The draft table has no mapping clause, so its names must align with the BO directly.
key client : abap.clnt not null;
key projectid : abap.char(10) not null;
title : abap.char(100);
description : abap.char(255);
status : abap.char(1);
startdate : abap.dats;
enddate : abap.dats;
erdat : abap.dats;
erzet : abap.tims;
ernam : abap.char(12);
aedat : abap.dats;
aezet : abap.tims;
aenam : abap.char(12);
" Required for `total etag CreationTimeStamp` + `etag master LastChangedStamp` in BDEF.
" Computed in CDS root view via dats_tims_to_tstmp(...); persisted on draft instances.
creationtimestamp : abap.dec(15,0);
lastchangedstamp : abap.dec(15,0);
include sych_bdl_draft_admin_inc;
}
ZDM_TASK_D も同様に、taskid と projectid(FK)を持たせ、タスク固有のすべての列にはアンダースコアなしの
エイリアスを付けます。ZDM_TIMEENTRY_D も同様に、entryid、taskid、projectid(FK)を持たせます。
ステップ 2 — CDS ルート(ZR_DM_*)
上から順に作成します。親を先に、その後に子を作成します。composition [0..*] of <child> as <name> を使用し、
マネージドシナリオでは on 句を付けません。
レガシーの DPC_EXT で Aedat → Erdat のフォールバックを行っていた場合(実行 1 で確認)、それをルートビューのタイムスタンプ計算に組み込みます。7.58 では @Semantics.systemDateTime.createdAt、
lastChangedAt、および localInstanceLastChangedAt を使用します。最後の注釈は、マネージドドラフト BO に必要なローカルインスタンス ETag フィールド(abp_locinst_lastchange_tstmpl)に付与するもので、SAP の RAP ジェネレーターが 758 で出力するものと同じです。S/4HANA 2023 で実環境で検証済みです。 localInstanceLastChangedAt と @Semantics.businessDate.from/to を含むスタンドアロンビューは、どちらも有効化できます。これらがないのは、真に古いリリース(ドラフト対応前の < 7.55)だけです。7.58 では削除しないでください。
@Semantics.systemDateTime.createdAt : true
dats_tims_to_tstmp(
zdm_project.erdat,
zdm_project.erzet,
abap_system_timezone($session.client, 'NULL'),
$session.client,
'NULL'
) as CreationTimeStamp,
@Semantics.systemDateTime.lastChangedAt : true
dats_tims_to_tstmp(
case zdm_project.aedat when '00000000' then zdm_project.erdat else zdm_project.aedat end,
case zdm_project.aedat when '00000000' then zdm_project.erzet else zdm_project.aezet end,
abap_system_timezone($session.client, 'NULL'),
$session.client,
'NULL'
) as LastChangedStamp
コンポジション CDS の有効化順序。 デフォルトの
batch_createは各オブジェクトを配列順にインラインで有効化するため、親のcomposition [0..*] of ZR_DM_TASK as _TasksがZR_DM_TASKの作成前に有効化され、"data sourceZR_DM_TASKdoes not exist" が発生します(実行 6 の再現例)。安全な方法は 3 つあります。実行に合うものを選んでください。
activateAtEnd: trueを指定したbatch_create(ARC-1 ≥ 0.9.5、PR #270)。すべてのオブジェクトの非アクティブなドラフトを書き込んだ後、最後に 1 回activateBatchを実行することで、SAP のアクティベーターが相互参照を一括で解決します。ARC-1 が対応している場合に推奨される方法です。ツール呼び出しは 1 回で、復旧手順も不要です。SAPWrite(action="batch_create", activateAtEnd: true, objects=[...])- 各ルートに対して個別に
SAPWrite(action="create")を実行し、最後に(すべての DDL ソースが作成された後で)SAPActivate(action="activate", objects=[...])を 1 回だけバッチ実行します。すべての ARC-1 リリースで動作します。- 手動で下から上へ進めます。親に対して
SAPWrite createを実行 → 子を先にSAPActivate→ 最後に親を有効化します。ツール呼び出しが増えるため、通常は不要です。同じ修正は ステップ 3 のプロジェクション(コンポジションチェーンを反映)にも適用されます。プロジェクションの 3 つ組はインラインで有効化せず、最後にまとめて有効化してください。
ステップ 3 — CDS プロジェクション(ZC_DM_*)— スキップ禁止
実行 1 でこのレイヤーが気付かれないまま欠落していた箇所です。ここで provider contract エラーが出た場合は、「プロジェクションを作成する」という意味であり、「コントラクトを削除する」という意味ではありません。
重要:provider contract transactional_interface はルートプロジェクション(ZC_DM_PROJECT)のみに指定します。 子プロジェクション(ZC_DM_TASK、ZC_DM_TIMEENTRY)では、as projection on … のみを使用し、redirected to parent を介して BO コントラクトを継承します。子にコントラクトを指定すると、有効化時に "inappropriate provider contract on …" が発生します(実行 2)。
ルートプロジェクション — コントラクトあり
シナリオに応じてコントラクトを選択します。
- マネージド + ドラフト + CUD(このスキルのデフォルト):
provider contract transactional_query(7.58 での実行 3 の証拠:プロジェクション BDEF とuse draftを組み合わせると、transactional_interfaceは拒否されます)。 - 読み取り中心、ドラフトなし、CUD なし:
provider contract transactional_interface。
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : 'Demo project — UI projection (root)'
@Metadata.allowExtensions: true
define root view entity ZC_DM_PROJECT
provider contract transactional_query
as projection on ZR_DM_PROJECT
{
key ProjectId,
Title,
Description,
Status,
StartDate,
EndDate,
Erdat, Erzet, Ernam, Aedat, Aezet, Aenam,
CreationTimeStamp,
LastChangedStamp,
_Tasks : redirected to composition child ZC_DM_TASK
}
子プロジェクション — コントラクトなし、redirected to parent あり
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : 'Demo task — UI projection (child)'
@Metadata.allowExtensions: true
define view entity ZC_DM_TASK
as projection on ZR_DM_TASK
{
key TaskId,
ProjectId,
Title, Description, Status, Priority, DueDate, AssignedTo, EstimatedHours,
Erdat, Erzet, Ernam, Aedat, Aezet, Aenam,
_Project : redirected to parent ZC_DM_PROJECT,
_TimeEntries : redirected to composition child ZC_DM_TIMEENTRY
}
ZC_DM_TIMEENTRY も同じ形式です(コントラクトなし、_Task : redirected to parent ZC_DM_TASK)。
子では
define root view entityではなくdefine view entityを使用する点に注意してください。define root view entityを使用するのは BO ルートのみです。
ステップ 4 — 空のビヘイビアプール(スケルトンはステップ 7 で自動作成されます)
空のグローバルクラスのシェルだけを書き込みます。ローカルハンドラークラス(lhc_project、
lhc_task、lhc_timeentry)はここでは事前作成されなくなりました。ステップ 7 の
SAPWrite action=generate_behavior_implementation(PR-C、ARC-1 ≥ post-2026-05-10)が、
存在しない場合はそれらを自動作成し、必要なハンドラーシグネチャとスタブをすべて
1 回の呼び出しで挿入します。CCDEF/CCIMP のための Run 2 の必須 ADT 貼り付け一時停止は
なくなりました。
SAPWrite(action="create", type="CLAS", name="ZBP_DM_PROJECT",
source="CLASS zbp_dm_project DEFINITION
PUBLIC ABSTRACT FINAL
FOR BEHAVIOR OF zr_dm_project.
ENDCLASS.
CLASS zbp_dm_project IMPLEMENTATION.
ENDCLASS.",
description="Behavior pool for ZR_DM_PROJECT BO",
package="<target_package>", transport="<transport>")
SAPActivate(type="CLAS", name="ZBP_DM_PROJECT")
想定される有効化の動作。 ここでの
SAPActivateは 「zr_dm_projectの ビヘイビア定義がありません」 を返す場合がありますが、これは想定どおりで問題ありません。 BDEF はまだ存在せず、ステップ 5 で作成されます。実際の失敗として扱わないでください。このクラスは BDEF が配置されると正常に有効化されます。ステップ 7(generate_behavior_implementation)では ハンドラーとともにクラスが再度有効化されます。次のいずれかを選べます。
- ここでは有効化をスキップ(書き込みのみ)し、ステップ 7 での有効化に任せます。
- または警告を受け入れ、ステップ 10 の一括有効化の一環としてクラスを再度有効化します。
これで完了です。ここでは CCDEF/CCIMP の書き込みも、ADT の一時停止もありません。ステップ 5 に進みます。
ステップ 5 — managed-with-draft ルールを組み込んだルート BDEF
Run 1 の呼び出し #22–31 では、managed-with-draft の 10 個のルールを苦労して見つけました。あらかじめ組み込んでおきます。
managed implementation in class zbp_dm_project unique;
strict ( 2 );
with draft;
define behavior for ZR_DM_PROJECT alias Project
persistent table zdm_project
draft table zdm_project_d
lock master
total etag CreationTimeStamp " <- required by `with draft`
etag master LastChangedStamp
authorization master ( instance )
{
create;
update;
delete;
field ( readonly : update ) ProjectId;
field ( readonly ) CreationTimeStamp, LastChangedStamp;
" composition associations: only `create` is allowed in the inline block
association _Tasks { create; with draft; }
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare; " <- 'determine' keyword required, not just 'draft action'
action approve_project result [1] $self; " <- DO NOT remove on activation failure;
" Step 7 (generate_behavior_implementation)
" auto-creates the lhc_project skeleton and
" injects the handler in one call.
mapping for zdm_project { … all field maps … }
}
define behavior for ZR_DM_TASK alias Task
persistent table zdm_task
draft table zdm_task_d
lock dependent by _Project " <- not _Project of root; use the cross-BO assoc you added in roots
etag dependent by _Project
authorization dependent by _Project
{
update; delete;
field ( readonly : update ) TaskId, ProjectId; " <- lock-by reference fields MUST be readonly
association _Project { with draft; }
association _TimeEntries { create; with draft; }
" NO draft Edit/Activate/Discard/Resume — those belong on the root only.
mapping for zdm_task { … }
}
define behavior for ZR_DM_TIMEENTRY alias TimeEntry
persistent table zdm_timeentry
draft table zdm_timeentry_d
lock dependent by _Project " <- not by _Task — chain must reach a `lock master`
etag dependent by _Project
authorization dependent by _Project
{
update; delete;
field ( readonly : update ) EntryId, TaskId, ProjectId;
association _Project { with draft; }
association _Task { with draft; }
mapping for zdm_timeentry { … }
}
TimeEntry から _Task にしか到達できず、_Project に到達できない場合は、
ZR_DM_TIMEENTRY(ルートビュー)に ZR_DM_PROJECT へのクロス BO
association to ZR_DM_PROJECT as _Project を追加する必要があります。ロック
チェーンは、lock master を持つエンティティで終端する必要があります。
ステップ 6 — プロジェクション BDEF
プロジェクションのビヘイビアエイリアスを宣言します。Run 6 では、7.58 でプロジェクション BDEF に 一見わかりにくい構文ルールが 2 つ適用されることがわかりました。あらかじめ組み込んでおきます。
- ルート BDEF で
with draftを宣言した場合は、use draft;を先頭に置きます(本体内ではありません)。 これがないと、use action Edit/Activate/Discard/Resume/Prepareが拒否されます。 use association _X { ... }ブロック内では、use create;ではなく、操作名だけ(create;)を記述します。useキーワードを置くのはトップレベルだけです。
ルートプロジェクション用の標準的なプロジェクション BDEF(managed + draft + CUD シナリオ):
projection;
strict ( 2 );
use draft;
define behavior for ZC_DM_PROJECT alias Project
use etag
{
use create;
use update;
use delete;
use action Edit;
use action Activate;
use action Discard;
use action Resume;
use action Prepare;
use action approve_project;
use association _Tasks { create; } " <- bare 'create;' inside, NOT 'use create;'
}
子プロジェクション(ZC_DM_TASK、ZC_DM_TIMEENTRY)には、同じ DDLS 内または個別の定義として、
それぞれ独自のプロジェクション BDEF ブロックが必要です。各ブロックで use association _Project/_Task を使用し、
ロックマスターチェーンの上方向のコンポジションを公開します。アソシエーション本文内では create; のみを記述する点は同じです。
ステップ 7 — 振る舞い実装を生成する(1 回の呼び出しで完全自律)
SAPWrite(action="generate_behavior_implementation", type="CLAS", name="ZBP_DM_PROJECT")
この 1 回の呼び出し(PR-C、ARC-1 ≥ 2026-05-10 以降)では、次の処理を行います。
- クラスメタデータを読み取り、
<class:rootEntityRef>を抽出して、関連付けられた BDEF を自動検出します(bdefNameの指定は不要です)。 - MAIN の
FOR BEHAVIOR OF zr_dm_projectと BDEF のmanaged implementation in class zbp_dm_project uniqueが一致することを相互検証します。不一致の場合は変更を拒否します。 scaffold_rap_handlersが使用するものと同じスキャフォールドエンジンをautoApply=trueで呼び出します。- 不足している
lhc_<alias>のスケルトン(CCDEF + CCIMP)を自動作成します。 - BDEF が必要とするすべてのアクション/判定/検証/認可について、
METHODS …シグネチャを挿入します。 - CCIMP に空の
METHOD … ENDMETHOD.スタブを挿入します。
- 不足している
- PR #257 の include= パスを介し、1 つのステートフルロックのもとで CCDEF + CCIMP を書き込みます。
- クラスを有効化します。
discovery、validation、scaffoldChanged、件数、activation.success を含む構造化 JSON レポートを返します。よく知られた「Local classes
of CL_ABAP_BEHAVIOR_HANDLER…」の古い有効化状態との連動が原因で有効化に失敗した場合、例外をスローする代わりに、具体的な復旧方法を示す activation.hint をレスポンスに含めます。書き込まれた CCDEF/CCIMP のソースは、どちらの復旧方法でもそのまま役立ちます。
明確でない理由で有効化に失敗した場合は、再試行する前に SAPDiagnose(action="object_state", type="CLAS", name="ZBP_DM_PROJECT")(PR #254)を実行し、インクルードごとの有効状態と非有効状態の差異を確認してください。ソース全体を出力せずに、同期が取れていない正確なインクルードを特定できます。
より低レベルの代替手段:既存の内容があるクラスに対してスキャフォールドを行う場合や、自動有効化せずにドライラン形式のプレビューを行う場合は、scaffold_rap_handlers を autoApply=true|false および明示的な bdefName とともに直接使用してください。新しい振る舞いプールには generate_behavior_implementation を推奨します。
ステップ 8 — edit_method でアクション本体を記述する
SAPWrite(action="edit_method", type="CLAS", name="ZBP_DM_PROJECT",
method="lhc_project~approve_project",
source="
METHOD approve_project.
READ ENTITIES OF zr_dm_project IN LOCAL MODE
ENTITY Project FIELDS ( ProjectId Status Aedat Aezet Aenam )
WITH CORRESPONDING #( keys )
RESULT DATA(projects).
MODIFY ENTITIES OF zr_dm_project IN LOCAL MODE
ENTITY Project
UPDATE FIELDS ( Status Aedat Aezet Aenam )
WITH VALUE #( FOR p IN projects (
%tky = p-%tky
Status = 'A'
Aedat = sy-datum
Aezet = sy-uzeit
Aenam = sy-uname
) ).
READ ENTITIES OF zr_dm_project IN LOCAL MODE
ENTITY Project ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(updated).
result = VALUE #( FOR u IN updated ( %tky = u-%tky %param = u ) ).
ENDMETHOD.",
transport="<transport>")
(COMMIT WORK は不要です。アクションのコミット時にフレームワークが自動的に保存します。)
ステップ 9 — サービス定義とバインディング
SAPWrite(action="batch_create", objects=[
{ type: "SRVD", name: "ZUI_DM_PROJECTS",
source: "@EndUserText.label : 'Demo Project Manager (V4)'\ndefine service ZUI_DM_PROJECTS {\n expose ZC_DM_PROJECT as Project;\n expose ZC_DM_TASK as Task;\n expose ZC_DM_TIMEENTRY as TimeEntry;\n}",
package: "<target_package>", transport: "<transport>" },
{ type: "SRVB", name: "ZUI_DM_PROJECTS_O4",
bindingType: "ODataV4-UI",
serviceDefinition: "ZUI_DM_PROJECTS",
package: "<target_package>", transport: "<transport>" }
])
注: SRVD が公開するのは
ZC_DM_*(プロジェクション)であり、ZR_DM_*(ルート)ではありません。ルートを公開している場合は、プロジェクションレイヤーが不足しています。ステップ 3 に戻ってください。
ステップ 10 — すべてを有効化する
SAPActivate(action="activate", objects=[
{ type: "DDLS", name: "ZR_DM_PROJECT" },
{ type: "DDLS", name: "ZR_DM_TASK" },
{ type: "DDLS", name: "ZR_DM_TIMEENTRY" },
{ type: "DDLS", name: "ZC_DM_PROJECT" },
{ type: "DDLS", name: "ZC_DM_TASK" },
{ type: "DDLS", name: "ZC_DM_TIMEENTRY" },
{ type: "BDEF", name: "ZR_DM_PROJECT" },
{ type: "BDEF", name: "ZC_DM_PROJECT" },
{ type: "CLAS", name: "ZBP_DM_PROJECT" },
{ type: "SRVD", name: "ZUI_DM_PROJECTS" },
{ type: "SRVB", name: "ZUI_DM_PROJECTS_O4" }
])
ステップ 11 — V4 サービスバインディングを公開する
SAPActivate(action="publish_srvb", name="ZUI_DM_PROJECTS_O4")
ステップ 12 — 状況に応じて必要となる手動手順:V4 サービスグループの登録
publish_srvb は ADT レベルでバインディングを有効化します。ほとんどの 7.5x システムでは、バインディングを使用する2 種類のランタイム URL 形式があり、Gateway ハブへの登録が必要なのはそのうち 1 つだけです。
| パス | publish_srvb 直後(IWFND の手順なし)の動作 |
|---|---|
Gateway ハブ — http://<host>:50000/sap/opu/odata4/sap/<srvb>_o4/srvd_a2x/sap/<srvb_short>/0001/ | IWBEP/CM_V4_COS/136 — Service 'ZUI_DM_PROJECTS' repository 'SRVD_A2X' is not assigned to group 'ZUI_DM_PROJECTS_O4' により 403 を返します。登録が必要です。 |
SRVD 直結 — https://<host>:50001/sap/opu/odata4/sap/<srvb>_o4/srvd/sap/<srvb>/0001/ | IWFND 登録なしで 200 を返します(Run 5 + Run 6 で検証済み)。パスセグメントは srvd_a2x/sap/<srvb_short> ではなく srvd/sap/<srvb> です。 |
判断:
- まずフェーズ 7 で SRVD 直結パスを試してください(手動手順は不要です)。動作する場合はそのまま進めてください。ステップ 12 は何もしなくてよくなります。
- 手動手順を実行するのは、対象システムで SRVD 直結パスも失敗する場合、または利用側(FE アプリ、パートナー)がハブパスを前提としている場合に限ります。
手動手順が必要な場合は、SAP GUI で次の操作を行います。
/n/IWFND/MAINT_SERVICE
Add Service → System Alias = LOCAL → Filter "ZUI_DM_PROJECTS*" → Get Services
Select ZUI_DM_PROJECTS_O4 → Add Selected Services → Package <target_package> → Transport <transport>
(Or, on systems with the V4-specific transaction:)
/n/IWFND/V4_ADMIN
→ Service Group "ZUI_DM_PROJECTS_O4" → Assign SRVD_A2X service "ZUI_DM_PROJECTS"
注:ハブパスは publish_srvb の直後に一時的な 503 CM_V4_RUNTIME/000(「サービスエイリアスのキャッシュが古くなっています」)を返し、その後の次回呼び出しで 403 COS/136 を返すことがあります。これは異なる症状です。503 はエイリアスキャッシュの不整合、403 は登録不足を示します。登録が原因だと判断する前に数秒待ってから再テストしてください。
ステップ 13 — フェーズ 5 → フェーズ 6 の契約確認
フェーズ 5f の成果物リスト契約を確認し、各ボックスのステータスをすべて出力します。
=== Phase 6 contract echo ===
Roots (CDS): [✓] ZR_DM_PROJECT [✓] ZR_DM_TASK [✓] ZR_DM_TIMEENTRY
Projections (CDS): [✓] ZC_DM_PROJECT [✓] ZC_DM_TASK [✓] ZC_DM_TIMEENTRY
Draft tables (TABL): [✓] ZDM_PROJECT_D [✓] ZDM_TASK_D [✓] ZDM_TIMEENTRY_D
BDEFs: [✓] ZR_DM_PROJECT (root) [✓] ZC_DM_PROJECT (projection)
Behavior pool (CLAS): [✓] ZBP_DM_PROJECT (with lhc_project, lhc_task, lhc_timeentry skeletons)
[✓] approve_project handler stub injected via scaffold_rap_handlers
[✓] approve_project body filled via edit_method
Service definition: [✓] ZUI_DM_PROJECTS (exposes 3 projections)
Service binding: [✓] ZUI_DM_PROJECTS_O4 (published at ADT level)
Manual steps remaining:
[✗ — waiting on user] /IWFND/MAINT_SERVICE V4 group registration
✗ で、免除されていないボックスがある場合は、フェーズ 7 に進まないでください。修正するか、ユーザーにエスカレーションしてください。
フェーズ 7 — 並べてスモークテスト
新しい V4 サービスが、レガシー V2 と同じデータを返すことを確認します。
7a. レガシー V2 のベースライン
curl -u "$USER:$PASS" "<base>/sap/opu/odata/sap/<legacy_service>/ProjectSet/\$count"
# expect: 5
curl -u "$USER:$PASS" "<base>/sap/opu/odata/sap/<legacy_service>/ProjectSet('PRJ-0001')/Tasks/\$count"
# expect: 3
7b. 新しい V4 — 動作する URL 形式を選択
両方を並行して試します。通常、SRVD 直接接続のパスはステップ 12 の手動登録なしで動作します。Gateway ハブのパスには登録が必要です。
# SRVD-direct (HTTPS, port 50001, /srvd/sap/<srvb>/ path) — preferred, no Step 12 needed
NEW_BASE_DIRECT="https://<host>:50001/sap/opu/odata4/sap/zui_dm_projects_o4/srvd/sap/zui_dm_projects_o4/0001"
# Gateway hub (HTTP, port 50000, /srvd_a2x/sap/<srvb_short>/ path) — needs Step 12
NEW_BASE_HUB="<base>/sap/opu/odata4/sap/zui_dm_projects_o4/srvd_a2x/sap/zui_dm_projects/0001"
# Smoke-test the SRVD-direct path first
curl -s -o /dev/null -w "%{http_code}\n" -u "$USER:$PASS" \
"$NEW_BASE_DIRECT/Project?\$top=1&sap-client=001"
# 200 = good; 403 = check Gateway hub registration; 503 = transient alias cache, retry
200 を返したパスを選び、フェーズ 7 の残りで使用します。デモ用に、動作する URL を RUN-NOTES に固定します。
7c. ドラフト V4 のキー指定 — 注意が必要
managed-with-draft BDEF から生成されたサービスでは、$metadata で公開されるエンティティキーは複合キーです。すべての主キー項目に加えて、IsActiveEntity(Edm.Boolean)が含まれます。Project('PRJ-0001') は 400 を返します。正しい形式は次のとおりです。
NEW_BASE="$NEW_BASE_DIRECT" # or $NEW_BASE_HUB after Step 12
# Counts — same as legacy V2 if the migration is byte-equivalent
curl -u "$USER:$PASS" "$NEW_BASE/Project/\$count?sap-client=001"
# expect: 5
# Composite key — note ProjectId AND IsActiveEntity, not just ProjectId
curl -u "$USER:$PASS" \
"$NEW_BASE/Project(ProjectId='PRJ-0001',IsActiveEntity=true)/_Tasks/\$count?sap-client=001"
# expect: 3
レガシー V2 のドラフトなしのセマンティクスと同じにする場合は、?$filter=IsActiveEntity eq true を使ってアクティブな行セットを明示的にフィルタリングします。
7d. アクションの呼び出し — CSRF と If-Match が必須
ドラフトエンティティに対する V4 のバインドアクションには、X-CSRF-Token(セッション Cookie を使用)と If-Match: <etag> の両方が必要です。If-Match がない場合は 428 CX_OD_PRECOND_REQUIRED が返されます。
# 1. Fetch CSRF token + session cookies
curl -s -u "$USER:$PASS" -c /tmp/cookies.txt \
-H "X-CSRF-Token: Fetch" \
"$NEW_BASE/?sap-client=001" -D - -o /dev/null | grep -i 'x-csrf-token'
# 2. Get the current etag for the target row
ETAG=$(curl -s -u "$USER:$PASS" -b /tmp/cookies.txt \
"$NEW_BASE/Project(ProjectId='PRJ-0003',IsActiveEntity=true)?sap-client=001" \
-D - -o /dev/null | grep -i 'etag' | awk '{print $2}' | tr -d '\r')
# 3. POST the action with both headers
curl -u "$USER:$PASS" -b /tmp/cookies.txt -X POST \
"$NEW_BASE/Project(ProjectId='PRJ-0003',IsActiveEntity=true)/com.sap.gateway.srvd.zui_dm_projects.v0001.approve_project?sap-client=001" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: <from step 1>" \
-H "If-Match: $ETAG" \
-d '{}'
# expect: 200 + entity with Status="A"
件数が一致しない場合は、CDS の where 句が誤っている可能性があります。見落としたフィルターがないか、レガシー DPC_EXT メソッドを読み直してください。両方のヘッダーを指定してもアクションが 412/428 で失敗する場合は、新しい BDEF アクション宣言を反映するために、サービスで新たに publish_srvb が必要な可能性があります。
リセット/再実行
このスキルを再実行します。フェーズ 6a では最初に <target_package> 内のすべてが削除されるため、複数回実行しても安全です。トランスポート <transport> は、明示的にリリースするまで D(変更可能)状態のままです。トランスポートもクリアするには、次を実行します。
SAPTransport(action="release_recursive", number="<transport>")
SAPTransport(action="create", description="ARC-1 RAP migration outputs (resettable)", transportType="K")
…その後、このスキルのスマートデフォルトにあるトランスポート ID を更新します。
エラー処理
| 症状 | 考えられる原因 | 対処方法 |
|---|---|---|
Phase 0 — rap.available=false | システムリリースが 7.54 未満、または ABAP-Cloud-mode が無効 | 移行を続行できません。ユーザーに伝えてください |
| Phase 0 — preflight DDLS create が 403 を返す | 対象パッケージの DDLS に対する S_DEVELOP ACTVT=01,02 が不足 | ユーザーが SAP GUI で SU53 を実行し、basis 管理者がロールを付与します |
| Phase 1 — 規則に基づく MPC クラスが見つからない | サービスで標準外の命名が使用されている、またはサブパッケージにある | TADIR クエリにフォールバックします(1a の代替パス) |
Phase 2 — DEFINE_<entity> 本文に解析できないヘルパーメソッドが含まれている | SEGW で define_<entity>_property_<n>(...) 呼び出しが使用されている | それらのヘルパーメソッドも読み取ります — SAPRead method="DEFINE_PROJECT_PROPERTY_1" |
Phase 3 — EXECUTE_ACTION 本文に複数の IF iv_action_name = 'X' 分岐がある | 1 つの DPC_EXT に複数の function import がある | 各分岐を個別の BDEF アクションに変換します |
| Phase 6 — 「released-API contract violation」で有効化に失敗する | レガシー DPC_EXT で未リリースの ABAP API が使用されており、CDS の where 句も同じものを継承している | リリース済みの同等 API に置き換えます(mcp__sap-docs__search で最新の API を検索します) |
Phase 6 — scaffold_rap_handlers が missing methods を 0 件返す | 有効化の順序が逆 | behavior pool のスキャフォールドより先に BDEF を有効化します — scaffold_rap_handlers は有効化済みの BDEF を読み取ります |
Phase 7 — V4 の Project/$count と V2 の ProjectSet/$count が異なる | CDS ビューに暗黙のフィルター(例:WHERE delivered_status <> 'X')があり、見落としている | DEFINE_PROJECT を再度読み、MPC に set_filter_* 呼び出しがないか確認します |
Phase 7 — V4 の _Tasks が空を返す | コンポジションの方向が誤っている、または参照整合性制約が不足している | ZR_DM_PROJECT のソースを調べます。composition of 句では、FK によって子を参照する必要があります |
Phase 7 — V4 エンドポイントが IWBEP/CM_V4_COS/136 … not assigned to group で 403 を返す | Phase 6 Step 12(V4 ルーティンググループへの登録)をスキップした | ユーザーが /n/IWFND/MAINT_SERVICE で ZUI_DM_PROJECTS_O4 の Add Service を実行します |
Phase 6 Step 5 — BDEF の有効化で association _X { update; delete; } が拒否される | アソシエーションのインラインブロックで許可されるのは create のみ | アソシエーションブロックから update/delete を削除します。子エンティティ自身の behavior ブロックで宣言します |
Phase 6 Step 5 — BDEF が draft action Prepare を拒否する | draft determine action Prepare でなければなりません(determine キーワードは必須です) | draft determine action Prepare に修正します |
Phase 6 Step 5 — BDEF が子エンティティの Edit/Activate/Discard/Resume 宣言を拒否する | Draft ライフサイクルアクションはルート(lock master を持つエンティティ)にのみ属します | ルートに移動します。子にはコンポジションアソシエーションと update;/delete; だけが必要です |
Phase 6 Step 5 — BDEF が total etag のない with draft を拒否する | Draft が有効な場合、RAP では total etag フィールドが必要です | ルートの behavior に total etag CreationTimeStamp(またはタイムスタンプフィールド)を追加します |
Phase 6 Step 5 — _Task 自体に lock master がない場合、BDEF が子エンティティの lock dependent by _Task を拒否する | ロックチェーンは lock master エンティティで終端する必要があります | 孫のルートビューに、BO をまたぐ association to ZR_DM_<root> as _Project を追加し、BDEF では lock dependent by _Project を使用します |
Phase 6 Step 5 — BDEF が lock dependent by _Project で使用される子エンティティの ProjectId 更新を拒否する | ロック参照フィールドは読み取り専用でなければなりません | 子の behavior ブロックに field ( readonly : update ) <field> を追加します |
Phase 6 Step 5 — BDEF がすべてのエンティティで authorization のない strict ( 2 ) を拒否する | strict ( 2 ) は明示的な権限宣言を必須にします | ルートに authorization master ( instance )、子に authorization dependent by _Project を追加します。Step 7(generate_behavior_implementation)が対応する lhc_<alias> のスケルトンを自動作成し、get_instance_authorizations のスタブを挿入します。事前に手動で作成する必要はありません。回避策として strict ( 2 ) を削除してはいけません。 |
Phase 6 Step 7 — generate_behavior_implementation が activation: { success: false, hint: <stale-active recovery> } を返す | よく知られた stale-active CCDEF/CCIMP の連動問題です。有効な include には SAP のプレースホルダーコメントがある一方、無効な include には実際のハンドラーが入っており、RAP が無効から有効への切り替えを拒否します。 | ヒントにある 2 つの復旧方法を使用します。(a)Eclipse の「Generate Behavior Implementation」ウィザードでクラスを一度有効化します(連動問題を回避できます)。または(b)SAPManage(action="delete", type="CLAS") + SAPWrite(action="create") を実行し、新しく作成したクラスに対して generate_behavior_implementation を再実行します。どちらの方法でも、書き込まれた CCDEF/CCIMP ソースは正しいものです。 |
Phase 6 — CDS が composition [0..*] of <child> as <name> on … を拒否する | 管理対象のコンポジションには on 句を指定しません。キーの紐付けは暗黙的、または子の with foreign key によって行われます | on 句を削除します。association to に切り替えてはいけません |
Phase 6 — CDS がルートビューの provider contract transactional_interface を拒否する | このコントラクトが有効なのはプロジェクションビュー(ZC_DM_*)のみです | このエラーはプロジェクションレイヤーが不足していることを意味します。ZC_DM_* プロジェクションを作成し(Step 3)、そこにコントラクトを指定します |
Phase 6 — CDS が tstmp_from_dat_tim(...) を拒否する | 関数名が誤っています | dats_tims_to_tstmp(...) を使用します(順序に注意してください。最初が dats、次が tims です) |
Phase 6 — 一括有効化で ED 064 — "no next/previous object found" 警告が出る | 7.58 における無害な一括有効化の特性です | 警告は無視してください。すべてのオブジェクトが active と表示されていれば、一括有効化は成功しています。実際のエラーも混在している場合は、単一オブジェクトごとの有効化を再試行してください。 |
Phase 6a — パッケージが空だと示された Phase 6a のリセット後に、SAPWrite create が「object exists」を返す | オブジェクトは対象パッケージではなく、別のトランスポートに存在します | すべてのパッケージを対象に TADIR で名前を検索します:SAPQuery sql="SELECT * FROM tadir WHERE obj_name = '<name>'"。見つかった場合は、実際のトランスポートを使って先に削除してから、作成を再試行します。 |
Phase 6 Step 2/3 — CDS が @Semantics.systemDateTime.localInstanceLastChangedAt または @Semantics.businessDate.* を「unknown annotation」として実際に拒否する | システムが 7.55 より前のバージョンです(Draft サポート以前)。7.58 ではこれらは有効です — S/4HANA 2023 で実機検証済みです。SAP のジェネレーターも 758 で localInstanceLastChangedAt を生成します | 7.58 ではそのまま使用してください。本当に古いリリース(< 7.55)の場合に限り、createdAt/lastChangedAt にフォールバックし、local-instance/businessDate アノテーションを省略します。 |
Phase 6 Step 3 — CDS が「inappropriate provider contract on ZC_DM_<child>」を拒否する | provider contract transactional_interface が子プロジェクションに指定されています | ZC_DM_TASK/ZC_DM_TIMEENTRY からコントラクトを削除します。コントラクトを指定するのはルートプロジェクション(ZC_DM_PROJECT)だけです。子では as projection on … のみを宣言し、上方向へのナビゲーションには redirected to parent を使用します。 |
Phase 6 Step 5 — BDEF の有効化で "key field PROJECTID expected at position 2, found PROJECT_ID"(または Draft テーブルで同様のフィールド名不一致)が拒否される | Draft テーブルが、BO エイリアスに正規化された名前(projectid、startdate)ではなく、スネークケースのフィールド名(project_id、start_date)で書き込まれています | BO エイリアスに一致するフィールド名を使用して Draft TABL を書き直します。アンダースコアを削除してください。有効テーブルではスネークケースを使用できます(BDEF マッピングが対応します)が、Draft テーブルでは使用できません。SAPWrite で update + activate を使用してください — Run 2 で正常に動作することが確認されています。 |
Phase 6a — 長い WHERE obj_name IN ('a','b','c',…) フィルターで SAPQuery が HTTP 400 を返す | ARC-1 ≥ 0.10.0 では、単純で長い IN (…) リテラルリストが自動的に分割されます(PR #254)。旧ビルドや複雑なフィルター(NOT IN、複数の SELECT、サブクエリ)では、引き続き 400 が発生します。 | パッケージ横断の存在確認には、SAPSearch(searchType="tadir_lookup", source="both", names=[...]) を優先してください(PR #256 + PR #270) — SQL は不要です。splitBrain 警告配列により、ADT と DB の不一致を 1 回の呼び出しで確認できます。より広範囲を検索する場合の代替手段として、devclass = '<package>' を指定した SAPQuery を使用します。 |
注記
- このスキルは、レガシーソースパッケージ、レガシーの SEGW プロジェクト、またはそのトランスポートには一切触れません。移行は並行して実行され、ユーザーが明示的に廃止するまで、レガシーは稼働し続けます。
- レガシーの DPC_EXT 本文に見慣れない API 呼び出しが含まれている場合は、
mcp__sap-docs__searchを使用してください。リリース済みクラウドでの相当機能を確認できます。 - ユーザーのリリースで利用可能な RAP 機能(例:
etag master、unmanaged save、lock master)を確認するには、mcp__sap-docs__abap_feature_matrixを使用してください。 generate-rap-service-researched.mdが標準の作成スキルです。このスキルの役割は発見と変換です。実際の作成と有効化をより専門的なスキルに委任することで、このスキルの役割を明確に保てます。