ABAP 開發踩坑與解法庫
用途:下個專案開工前通讀一遍,排障時按症狀查。本技能只收「跨項目可複用」的經驗,環境地址、請求號、物件清單見項目側總綱(如 sap-abap-project-ops)。
完整 120 條(含程式碼片段與出處)見 references/casebook.md。
怎麼用:新專案開工 → 直接翻 §11 開工 checklist(按時間順序執行);已開工遇到問題 → §10 症狀速查 定位到分節;想深挖某條的完整程式碼與出處 → references/casebook.md。
三條元規則(比任何單條坑都重要):
- 報錯文案經常與真實原因無關 —— ABAP 編譯器的報錯點往往是「它解析到的位置」,不是「你寫錯的位置」。先假設是段序/關鍵字順序/型別不匹配,而不是語法本身。
- SAP 最危險的不是報錯,是靜默失效 —— 「返回 S、激活 200、編譯通過」三重假象下數據悄悄錯。凡涉及 BAPI、
SELECT INTO TABLE、EXTENSIONIN、文字元素、增強欄位,一律做落庫對帳,不信返回值。 - 失敗必查 ST22,不猜 —— 短轉儲的
Error analysis直接給出procedure "XXX" "(FORM)"與CX_SY_*異常類,比逐個 FORM 試快一個數量級。
0. 十條鐵律(違反即返工)
- 改任何已存在物件,第一步回讀現網。用戶常在 SE80 直改生產物件且不回寫歸檔,本地歸檔只作比對、不作基線。
get_source→ 與備份 diff 確認 0 差異 → 打補丁 → 回讀再 diff → 反向覆蓋歸檔。 - 文字元素同屬基線,
get_source不含 TEXTPOOL。覆蓋式推文字前必須read_textelements取激活版全文存檔、與本地 JSON diff、人工合併再推。否則用戶在 SE38 前台改的文案被無痕沖掉,源碼 diff 完全看不出來。 - 絕不直接 UPDATE / MODIFY 標準表。改已過帳 FI 憑證走
FI_DOCUMENT_CHANGE(組FACG,T_ACCHG只填FDNAME+NEWVAL,抬頭不傳I_BUZEI、行項目才傳)。 - 請求號不自己造。推可傳輸物件缺
CORR_NR⇒ 停手報告,等用戶給號;新建 TADIR 物件前先報「類型+建議名+TADIR 查重結果+編號延續理由」,點頭再建。已掛請求的既有物件後續改動複用原號,不再問。 - 不用
Update*類寫介面做試探性呼叫。它會真寫——曾用UpdateFunctionModule只傳函數體「試試」,把 inactive 源碼覆蓋成 620 字元殘版。探針只走唯讀介面。 - 交付四步:生成 → 推源碼 → 推文字元素(
PROG/PX須單獨激活)→ 生成 GUI 狀態(CUA)。文字元素不激活 = 運行時TEXT-xxx恆空。 - 生產源碼註解:留骨架、刪敘述。保留程式頭(單位/作者/日期/摘要/變更記錄)+
*&---框線 + FORM 橫幅 +-->/<--形參文件 + 判據型短註解;刪除設計依據、口徑說明、聯調實測、TODO 理由、scripts/work/ADT/ZTEST_*引用。剝離後必須 diff 證明程式碼本體零改動。 - 測試/探針物件測完即刪。先
get_source留檔backup/→ 刪 → 回查TADIR清零。只刪本輪自己建的;他人建的、已掛包掛請求的、在用的 DEMO 一律不動。 - 先查鎖、先備份再寫 SAP。憑據檔案不外傳、不回顯、不寫進交付物。
- 用戶說「數據沒問題」時,更要認定程式有問題。用「報錯金額+張號」反推輸入特徵,寫探針逐字複刻該報錯;複刻成功即鎖定根因。
1. 激活與寫入通道(ADT / MCP)
- HTTP 200 ≠ 激活成功 — 成敗寫在 body:必須顯式檢測
activationExecuted="false"並拋錯;驗證激活唯一可靠方式是回讀 include 或?version=active與 inactive 雙版本比對。TRDIR-RSTAT在 S/4 不寫值。 - 假成功最難發現 — 舊腳本按
<chklst:shortText>/<msg>text</msg>正則抓訊息,真實結構是chkl:前綴 +<shortText><txt>+ msg 帶屬性 ⇒ 一條訊息都打不出來,連報三次「激活成功」實際是 131 字元空殼。抓不到訊息就必須當失敗。 - 文字元素是獨立可激活物件
PROG/PX— 激活父程式不連帶;push_textelements若只有GET→LOCK→PUT→UNLOCK沒有 ACTIVATE,PUT 只寫 inactive 版。傳PROG/P時 SAP 認為無可激活內容,回activationExecuted="false"且無任何 msg ⇒ 靜默無效。激活介面不接受corrNr(帶了 400)。 push_textelements的 PUT 是整塊覆蓋,推序不能反 — 新增符號:先文字元素後源碼;刪除符號:先源碼後文字元素。TE_FILE必須連同既有 selections 一起給全。- 文字元素只有 inactive 版時,程式須重新生成才生效 — 只推文字元素 + 再 PUT 內容相同的源碼不會重新整理。改動源碼一行(哪怕註解)後重推,或重建程式。
- 三種「假鎖」:① 報「已在使用者 xxx 的請求 yyy 中鎖定」其實是漏傳
transport_request/corrNr;②UpdateDataElement報locked by another user其實是 400「已存在」——handler 的isAlreadyExistsError()只認英文,中文訊息判不出,撞上 403 兜底被譯成「被鎖」(⇒ ZH 環境下 MCP 改不了已存在的資料元素,UpdateDomain無此前置故不受影響);③403 … not specified for this client是集團不對。 - 真鎖只有 SM12 能刪 —
ESDICT/ESRDIRE/SEOCLSENQ三類鎖掛在 ADT 會話上,DEQUEUE_ALL、_scope='3'、DEQUEUE_EESDICT全部無效。ENQUEUE_READ確認後請使用者在 SM12 手工刪,立刻重跑;別等會話逾時。 - 兩句 403 含義完全不同 —
User XXX is currently editing ZFIDxxx= 真人開著 SE80,等;Object ... locked in task DEVK90011x not specified for this client= 集團不對,換請求所屬集團重推。混為一談會白繞一圈。 - ADT LOCK 的
Accept頭必須帶 datatype — 只發application/xml時 SAP 已加鎖成功但響應 406 拿不到LOCK_HANDLE⇒ 鎖洩漏,之後所有請求報「使用者 X 目前編輯」,一直騙你以為是別人佔著。正確值:application/vnd.sap.as+xml;dataname=com.sap.ad.lock.result。 - 腳本
fail()裡process.exit()跳過finally⇒ 入隊鎖殘留 — 類推送腳本異常退出後SEOCLSENQ鎖能留 40 分鐘,之後所有 LOCK 報 403 像「使用者開著 SE80」。寫腳本一律先 UNLOCK 再退出,或改process.exitCode = 1。 - 不要替使用者強刪編輯鎖 — ADT 刪鎖會丟對方未儲存的修改。
- include 是獨立 ADT 資源 — POST 打到
/programs/programs一律建成可執行程式(TRDIR-SUBC=1),之後改不回來;include 打/sap/bc/adt/programs/includes(根元素<include:abapInclude>)。主程式與 include 必須同一條 activation 請求,否則互報未定義。 INSERT REPORT ... STATE 'A'造出的 include 會把源碼寫成 active 版 — 之後改 inactive 版,主程式激活仍按舊 active 編譯,報的錯指向舊程式碼。改完須INSERT REPORT 'X' FROM lt_src STATE 'A' PROGRAM TYPE 'I'.(STATE必須在PROGRAM TYPE之前)重新整理 active 版。- 本機代理會把 ADT 請求打掛 — 表象與「連不上 SAP」一模一樣(
socket hang up/ CSRF 失敗)。spawn MCP 子行程前刪*_proxy並設NO_PROXY=*;寫請求一律proxy: false。 - ADT 無 dynpro / GUI Status / Menu 資源 — 但 CUA 可程式碼化(
RS_CUA_INTERNAL_FETCH+_WRITE,state='A',寫回即生效)。D020S是 dynpro 螢幕頭不是 GUI 狀態表;RS_CUA_GET_STATUS不可用於判定,只認RS_CUA_INTERNAL_FETCH(10 個必輸 TABLES 全給,ORDER BY pposition防 25 行截斷)。
2. 語法硬約束(一次過編譯)
- FORM 形參四禁忌:① 不能寫
TYPE c LENGTH n(報Numeric literals cannot be used as field names)⇒ 用TYPE char<n>或 DDIC 類型;② 定長形參收不同長度實參編譯期報 incompatible,文字符號例外但運行期仍 500Type conflict when calling a FORM⇒ 跨長度用泛型TYPE c;③ 不能內聯TYPE STANDARD TABLE OF(形參被算成 8 個,報Different number of parameters)⇒ 先TYPES命名;④FIELD-SYMBOL在 FORM 裡宣告是區域性,跨 FORM 必須改全域性。 FORM ... TABLES必須寫在USING/CHANGING之前 — 寫成USING ... CHANGING ... TABLES報Field "TABLES" is unknown.(把關鍵字當形參名)。PERFORM呼叫側同理。CALL FUNCTION段序強制 —EXPORTING → IMPORTING → CHANGING → TABLES → EXCEPTIONS。把TABLES寫在IMPORTING之前報Unable to interpret "EV_OBJNR",完全看不出是順序問題。被調方EXPORTING形參要寫在呼叫方IMPORTING段,寫反報CALL_FUNCTION_PARM_UNKNOWN——極易誤判成「FM 介面沒生成」。- 多欄位 SELECT 必須逗號分隔,
@宿主變數必須轉義 — 報elements in the "SELECT LIST" list must be separated using commas。UPDATE ... SET多賦值同理;WHERE裡常數(含CONSTANTS)同樣要@。 INTO與UP TO n ROWS位置以激活器報錯為準 — 帶 JOIN/子查詢/SQL 表達式的語句屬嚴格模式,強制INTO在最後(SELECT 563);UP TO n ROWS必須在WHERE之前。兩種寫法在不同語句上表現不同,照抄記憶會翻車。ASSIGN ... CASTING TYPE c LENGTH <變數>不支援 — 長度必須是字面量;去掉LENGTH又在下標越界時 DUMPInvalid subfield access。正解用cl_abap_container_utilities=>fill_container_c( )(與標準函數READ_CONTAINER_C互逆)。DESCRIBE FIELD的TYPE必須寫在COMPONENTS之前 — 只想取元件數也得寫:DESCRIBE FIELD cs_data TYPE lv_type COMPONENTS lv_count.CONSTANTS必須帶VALUE—CONSTANTS: c_x TYPE flag.報"VALUE ..." is not specified.MESSAGE TEXT-xxx TYPE 'S' WITH n不合法 — 文字符號不能作帶WITH的訊息文字。走輔助 FORM:lv_text = pv_text → REPLACE '&' IN lv_text WITH lv_num → MESSAGE lv_text TYPE 'S'.- 定長類型沒有
char<n>這個名字 —TYPE char1000/char55/char220全部非法,報Type "X" is unknown;一律TYPE c LENGTH n。類型名猜錯=浪費一輪激活,不確定就查DD04L或get_source。 class=>method只能用於靜態方法 — 例項方法必須CREATE OBJECT lo. lo->method( ),否則報can only be specified with static methods。- 字串模板不能作
PERFORM ... USING實參 — 報Field "|" is unknown;先賦給變數再傳。 CALL TRANSACTION ... WITH f1 v1不是合法語法 —WITH只接AUTHORITY-CHECK。傳參只能走USING bdcdata;SM30 首屏只有表名一個欄位(VIMDYNFLDS無更新模式欄位),進修改模式用VIEW_MAINTENANCE_CALL+action = 'U'。REPLACE ALL OCCURRENCES OF ... WITH ''在本類型系統會 endless loop — 刪空格/刪字元改逐字元DO strlen TIMES+lv_txt+lv_i(1)過濾。CONCATENATE不接受數值型 —lv_cnt TYPE i報 must be character-like;內建函數(strlen/lines)不能直接進&&(報The syntax for a method specification is objref->method,一次報一批誤導成箭頭語法錯)。數值一律用字串模板|...{ lv_cnt }...|。$TMP探針內不能定義 FORM;方法體外的註解不會被儲存 — 探針激活報Statement is not accessible(CheckProgram反而不報);類裡ENDMETHOD與下一個METHOD之間的註解報unknown comments which can't be stored。SELECT-OPTIONS名 ≤ 8 字元 — 9 字元首次激活被拒。改短名要同步改文字元素selections條目名(舊變式存舊名會被靜默忽略,只提示不報錯)。
3. 靜默失效清單(最貴的一節)
凡是「返回 S / 激活 200 / 編譯通過」但結果不對,先來這裡對號。
3.1 資料錯位與丟失
SELECT 顯式列清單 INTO TABLE <預宣告整表類型>按位置賦值 — 型別恰好相容時不報錯不告警:DD07T的DOMVAL落進DOMNAME、COEP的WKGBTR落進MEGBTR(金額恆空)、ZTEPIC3A報component "ZJK" not compatible with "ZJYRQ"(靠_act_dbg拿座標)。一個坑在 5 份報告裡復發。 解法三選一:SELECT */INTO TABLE @DATA(...)內聯(結構由列表定型)/ 自建列序一致的窄結構;或INTO CORRESPONDING FIELDS OF TABLE。LEFT JOIN右表欄位寫在WHERE裡 ⇒ 語義退化成INNER JOIN— 右表無匹配時為 NULL,條件不成立。過濾條件移進ON。DELETE ADJACENT DUPLICATES ... COMPARING <非鍵欄位>會吞資料 — 條件表 A017 主鍵含DATBI、KNUMH不是鍵,按COMPARING knumh去重把同 KNUMH 的後續有效期段全刪了。先SORT BY全鍵再COMPARING全鍵。AT NEW/AT END OF按物理相鄰判組,ALV 回寫後必失效 —REUSE_ALV_*會就地重排t_outtab(使用者按訊息列排序即觸發),同組不再相鄰、AT END OF多次觸發。分組改「外層遍歷去重鍵 × 內層按鍵收集」,組邊界只認業務鍵。APPEND會改寫sy-tabix—APPEND INITIAL LINE ... ASSIGNING <fs>之後取sy-tabix得到的是段序號不是源行號 ⇒ 區間被放大,128 行輸出成 558 行(同一記錄重複 43 次)。迴圈首行先lv_tabix = sy-tabix.。READ TABLE ... WITH KEY未命中把sy-tabix置 0 — 隨後APPEND sy-tabix TO lt_del記進刪除清單,DELETE ... INDEX 0直接 DUMPTABLE_INVALID_INDEX。迴圈首行存行號 + 刪除前IF idx >= 1 AND idx <= lines( )。- 內聯
DATA(...)宣告在IF分支裡,條件不成立時內表從未建立 ⇒ 後續LOOP短轉儲。宣告提到方法頭部,或取數前置空判斷。
3.2 BAPI / 標準函數「返 S 或不報錯但沒寫進去」
- BAPI 的
x結構必須與原結構成對傳 — 只傳depreciationareas漏depreciationareasx⇒ 填了 AFASL 就報No change parameter given for depreciation area 01。缺 x 結構不報語法錯,只在資料條件滿足時觸發。 - SD 增強欄位識別結構的資料元素必須是
CHAR1—SD_SALESDOCUMENT_CHANGE走GET_CHANGEABLE_FIELDS,只滾WHERE rollname = 'CHAR1' OR 'CHAR01'的欄位;用BAPIUPDATE的全部被排除 ⇒ 返回 S、值不變、零報錯,而建立路徑不走此清單 ⇒ 典型「建立能寫、修改寫不進」。不可改回BAPIUPDATE。 BAPIPAREX-VALUEPART1只有 240 字元 — 標準函數按偏移連續讀VALUEPART1~4(合計 960),只填第一段則尾部靜默截斷(BAPE_VBAP實測 271 字元,後 3 個欄位全空、無報錯)。按 240 分四段填。- 借貸項/無計劃行訂單類型:數量只認
TARGET_QTY—BAPISDITM裡沒有任何欄位的參考欄位是KWMENG;該類項目類別在TVEPZ無配置 ⇒ 不產生計劃行,下發的BAPISCHDL被靜默丟棄,返 S 但KWMENG=0、NETWR=0、VBEP0 行。判據用TVEPZ有無條目,不能用TVAP-LFREL(Z001與Z014的LFREL都為空)、也不能用VBTYP。讀取側同一坑:VBAP-KWMENG恆 0、數量在VBAP-ZMENG⇒ 回填口徑「KWMENG非空優先、為空回退ZMENG」。 BAPE_*首欄位是鍵位,修改模式漏傳即清空單號 — 標準函數把EXTENSIONIN值move-corresponding進E_VBAKKOM,VBELN留空就把單號覆蓋成空 ⇒V1 045後跟V4 219(根因看V4 219前面那條 E 訊息)。- SD 參考單據是三件套 —
REF_DOC+REF_DOC_IT+REF_DOC_CA(參考憑證類別);只傳前兩個報V2 068 不含憑證類別 但含 C(&2空)。行號填 0 或單據不存在是同一條訊息但&3為空。類別取被參考單據自身的VBAK-VBTYP,不是抬頭VGTYP。 SD_SALESDOCUMENT_CHANGE入口對兩個表各自排序 — 刪除行必須ITEM_IN與ITEM_INX都填ITM_NUMBER;只填ITEM_INX會錯位,報完全不相關的T184 沒有可用的項目類別。BAPI_USER_EXISTENCE_CHECK在本類系統對任何帳號都返 0(連ZZZZZZ999都通過)⇒ 校驗形同虛設,單據轉給無效帳號後兩個介面都看不到。直查USR02+ 長度防護(0 < len <= 12)。DELETE_TEXT對不存在的文字RAISING NOT_FOUND— 標準函數把該 E 訊息追加進BAPIRET2,被frm_check_return判失敗 ⇒ 「清空」反而把介面打成 E。清空前先查STXH(TDOBJECT+TDNAME+TDID+TDSPRAS),不存在就跳過(順帶讓操作冪等)。另:SD_SALES_DOCU_MAINTAIN裡清空功能碼是003(DELETE_TEXT),傳&&/005只是把文字重寫成一條空行。- 新增定價條件不能指定步驟號 — 硬編碼
COND_ST_NO='001'而ZP01在STUNR=010⇒ 報「定價類型不在步驟編號 001 中」。留空由定價過程自動定位;用「條件是否已存在」驅動UPDATEFLAG的I/U。 - 介面二次拆張時
OBJ_KEY只回一個憑證號 ⇒ 張數少報、按行回寫指錯憑證。OBJ_KEY佈局是憑證號(10)+公司程式碼(4)+會計年度(4),年度在+14(4)(常被誤寫+10(4)取到公司程式碼)。 BSEG-BUZEI是NUMC(3),單張憑證 999 行物理上限,BAPI 實際只吸納 990 行 — 分片上限取989 明細 + 1 軋差;邊界必須覆蓋 999/1000/1997(1997 按>999判會切出 1000 行而溢位)。BAPIACHE09沒有EXCH_RATE欄位 — 雙幣(00+10 行)只給 00 行帶匯率時,系統按 OB08 重定抬頭匯率(填 7 落 8.28)。CURR_TYPE='10'行也必須帶exch_rate,明細行與軋差行兩處都要。- 雙幣憑證每個
ITEMNO_ACC必須給兩條CURRENCYAMOUNT,且每種幣種小計為 0 — 缺10行報Inconsistent currency information;拆分產生的軋差行同樣要補。 UMSKZ在三個 BAPI 結構裡都沒有欄位 — 票據類(W/S)走標準 BAPI 通道會被靜默丟棄、科目記錯。只能經EXTENSION2增強落庫,或整張走POSTING_INTERFACE_*(FB01 批次輸入)。READ_TEXT的NAME形參是TDOBNAME(70),實參TDNAME(16) 觸發執行期類型衝突 — 編譯通過、空入參正常、一命中資料就 500Type conflict during a function module call。形參寫TYPE tdobname,或直讀STXL簇表。
3.3 文字 / 描述 / 列標題變空
- 欄位用匿名內建類型 ⇒
DD03T文字為空 ⇒ ALV 列標題一片空白 — 欄位必須掛資料元素(掛上後DD03T變 0 行是正確狀態,文字上移)。結構欄位文字只能靠 DDL 逐欄位@EndUserText.label;巢狀的STRU/TTYP型欄位永遠沒有DD03T文字(SAP 標準結構同樣如此)。CreateStructure的fields[].description不入DD03T。 - 文字元素未激活 / 只有 inactive 版 ⇒ 執行期
TEXT-xxx恆空 — 表現為 ALV 列標題退回 DDIC 短文字、狀態列為空。 - 域只定義型別沒維護值集 ⇒ 選擇螢幕 F4 彈不出候選 — 查
DD07T必須帶AS4LOCAL='A'(否則取到非激活行)、ORDER BY VALPOS(否則得1,10,11,2,3…字典序)、語言回退域主語言DD01L-DOMMASTER(欄位名不是MASTERLANG)。 MESSAGE ... WITH對整條文字約 70 字元上限 ⇒ 長 BAPI 錯誤被靜默截斷成Field Reas...,誤導排查方向。改用短前綴 + 字串模板接原文(≤220 字元,對齊BAPI_MSG)。BAPIACGL09-ITEM_TEXT(50) /BAPIACHE09-HEADER_TXT(25) 靜默右截斷 — 拼「前綴+後輟」先把前綴截到長度-後輟長度。- DDIC 元件名 30 字元靜默截斷 — JSON 鍵名對映時會莫名丟鍵。
3.4 許可權與過濾
AUTHORITY-CHECK傳空值通常無授權 ⇒ 相關記錄永久不可見 — 角色裡不會授EKGRP = space。組織層欄位為空時不做校驗直接放行:IF lv_ekgrp IS NOT INITIAL. AUTHORITY-CHECK ... ENDIF.。用行級過濾而非整屏拒絕,否則無權使用者連事務碼都進不去。MODIF ID只控顯隱,隱藏欄位已填的值仍參與過濾 — 隱藏場景用 RANGE 在取數側顯式攔住。
4. 事務與 LUW
EXPORT ... TO DATABASE(cluster 日誌)受事務控制 — 「落底表 → 建憑證 → 失敗回滾」使日誌與底表同處一個 LUW,ROLLBACK把indx=01入參快照一起吃掉(indx=02還在)。且底層過帳 FM 內部還會BAPI_TRANSACTION_ROLLBACK,所以「刪掉自己的 ROLLBACK」或「調順序」都無效。 ⇒ 解法:在唯一的寫庫動作之前、且在調 BAPI 之前插一句獨立COMMIT WORK,ROLLBACK原樣保留。附帶收益:調 BAPI 前先結算自身 LUW 本就是規範姿勢。- 要原子性就別各自提交;要獨立提交就保留回讀 —
SD_SALESDOCUMENT_CHANGE的CALL_FROM_BAPI='X'把更新掛進 update 佇列,未提交查不到 ⇒ 刪掉COMMIT WORK AND WAIT後保留的SELECT回讀校驗必然誤判「標誌未更新」。過帳失敗分支必須補ROLLBACK WORK,否則掛起的 SD 更新會懸空到下一次提交被誤落庫。 - 平臺快照只綁 FM 參數,函數內區域性變數一律不綁 — 報文在
lv_req/lv_resp裡就平臺上看不到;監控器serialize不帶name_mappings,輸出 DDIC 大寫名而非契約報文。業務側自建報文日誌表 + 一行save( )寫在報文唯一產生地(frm_send末尾),獨立提交正好補上平臺被回滾吃日誌的短板。
5. 彈框 / F4 / ALV / 選擇螢幕
POPUP_GET_VALUES的RETURNCODE語義反直覺 —'A'= 取消;空格 = 確認且FIELDS已回填(LSPO4U01:IF ok_code_save = 'CANC'. returncode = 'A'. ELSE. fill_values. returncode = space.)。寫成IF lv_rc <> 'A'把「確認」當「取消」⇒ 現象是點彈窗確認後毫無反應(無訊息、無動作、無轉儲)。POPUP_GET_VALUES只在欄位掛得住 DDIC 搜尋幫助時才彈 F4 — 判據F4IF_DETERMINE_SEARCHHELP(0=有 / 2=欄位在但無幫助 / 1=欄位不存在)。不要靠改成SKA1-SAKNR藉幫助——會引入 ALPHA 補零,'8000'變'000008000',校驗誤報且存庫格式變。 ⇒ 改用POPUP_GET_VALUES_USER_HELP+F4_FORMNAME+F4_PROGRAMNAME = sy-repid,欄位屬性不變。出口 FORM 由 FM 以 5 個USING呼叫(tabname/fieldname/display_flag/returncode/value),把選中值寫回第 5 個形參並把returncode置'F'彈框才重新整理(拿不到sy-stepl,不要走 dynprofield 回填)。POPUP_GET_VALUES不支援按條件隱藏欄位 —SVAL只能控FIELD_ATTR(必輸/唯讀)。按類型只裝載對應欄位集,使用者改了類型就按新類型重彈(類型未變即退出,不會迴圈)。F4IF_INT_TABLE_VALUE_REQUEST傳FIELD_TAB時每行必須能推出INTTYPE— 推導鏈TABNAME → INTTYPE → DATATYPE → DOMNAME → ROLLNAME,任一環缺就預設c引發轉儲。傳VALUE_TAB即跳過推導,建議直接用。RS_VARIANT_VALUES透過SELECT_OPTIONS傳參會被F4IF_SHLP_EXIT_EXAMPLE強制輸出所有選項值 ⇒ 大量資料時撐爆前端。用SHLP無SHLPEXIT的域進行保留。CL_GUI_ALV_GRID的set_ready_for_input傳1觸發ENTER,不檢查即出口 — 始料未及的寫入,所有掛了DATA_CHANGED的欄位被清空。改用REUSE_ALV_GRID_DISPLAY的佈局結構UCOMM。- ALV 可編輯欄位用
DRAGDROP時CHECKBOX全選會失效 — 系統在transfer_data_to_spreadsheet時重新整理格子,完整保留清單。 LS_HANDLE-DRAGDROP與handle_drag的EVENT互動時,EV_ROW_NO常被返回 0 ⇒ 起始位置索引不起作用。用CHANGING保留內部對映表。- 列標題在
CATALOG_ENTRY中的OUTPUTLEN必須 ≤DD03L的OUTPUTLEN— 違反時列被隱藏、返回空,無報錯。 REUSE_ALV_VARIANT_F4在T_REPOT未正確傳入時直接崩潰 —T_REPOT必須與REPID一致;不一致時FCAT_COMPLETE返回空。RS_REFRESH_FROM_SELECTOPTIONS不會清理RANGES中的空白行 — 循環WHERE條件時空白行引起INCLUDE失效,過濾條件被跳過。DD_SHLP中的OPTION不支援 'EQ',只支援 'E' — 帶 'EQ' 的RANGES在 F4 時直接轉儲。
6. JSON 與 SAP 閘道
- SAP 閘道 OData 名稱輸出大寫 —
json_name不影響。要去小寫加~text節點的「serialize」加上name_mappings(結構型別為NAME_MAPPING)。 /IWFND/MAINT_SERVICE激活時 活動 /Upload Metadata若原EDMX已經激活則不覆蓋 — 必須先停用服務再啟用,然後重新啟用。cleanup無法解決。/IWFND/IWSV的重新生成會覆蓋MP/DPC的所有自定義修改 — 永遠保留*_DPC_EXT中的修改,_DPC由閘道自定義覆蓋且不寫回。- Satety 檢查:
max_hits型別為NUMC,數字型別不匹配時,/IWFND/ERROR返回200但資料為空 —max_hits明確定義在$top為INT4時返回正確資料。 - 自定義
MP實體中,KEY欄位<16 字元不被允許 — 使用GUID。 SEGW建立的專案,CREATE方法中的BOPF實體自動附加increase為真 ⇒UPDATE等對應操作時報key has key structure。每次CREATE需手動設置increase = abap_false。
7. DDIC 建置物件
CreateStructure不產生DD03T;Activate才產生 —fielddesc只寫DD04L、DD03L,不寫DD03T;僅在Activate激活後生成。CreateDomain的ddlanguages只後端覆蓋,無法從ddlanguages修改域文字 — 域文字從DIALOG寫入DD01L-DDTEXT。CreateDataElement在domain已存在但DOMNAME類型不匹配時報data element includes an invalid name— 域類型必須與ES_DD07V-DOMVAL匹配。域值長度與類型不匹配會報domain value can not be assigned。Activate對ENQMODE(鎖物件)SWITCH 時,S模式只檢查TABLE部分,而E模式檢查 ——全域啟用。- 鎖物件包含
VIMDYNFLDS時Activate會加上對應VIEW及FIELD的所有鎖模式 — 實際使用SM30時不再需要自定義鎖邏輯。 GETMETHOD在服務巢狀用法時只作用自身層次,不作用下層 — 啟用後需要迭代處理。TRWBO_CORR_LOG不支援對CreateDomain的版本管理 — 建立後無法用delete清空。update_function的TRFUNC在模組池(M)中不維護 — 僅在功能組中可用。
8. 打補丁腳本陷阱
- 補丁推
STABLE STANDARD內部表前沒有REFRESH清空,APPEND 會累加 — 第 2 次部署後資料翻倍。每次推之前REFRESH lt_data。 INSERT INTO ... VALUES提交不自動,關聯COMMIT WORK— 批次寫入需顯式COMMIT,否則 SQL 在執行環節未完成。FIELD-SYMBOL記憶體的LOOP資料或ASSIGN的變數在INSERT後失效 —INSERT重建後指向舊位址。走REF或重新賦值。- 定時補丁
JOB不可直接DELETE—JOB_CLOSE會將STATUS變為S,解排必須JOB_CLOSE+JOB_OPEN。 BDC的MSGID若與T100表不一致,補丁回寫失敗且跳過錯誤 — 只檢查IKPF的MSGID對應T100表。SM30維護檢視時對VIEW_MAINTENANCE_CALL的ACTION若為*,則補丁匯入後會自動建立表記錄 — 即使UPDATE沒被定義。VIEW_MAINTENANCE_CALL先DELETE刪除TABSTRIP再INSERT時容易出現唯一鍵衝突。
9. ST22 與探針排查手法
ST22短轉儲ERROR在SOURCE CODE中只顯示 前 100 行 程式碼 — 必須從LONG TEXT或DUMP_ANALY中讀取完整上下文;SOURCE_CODE根據偏移來定位,不能直接用來查。CX_SY_DYN_CALL_*轉儲的DYNAMIC TABLE參數,在ST22中檢視為空白 — 因內部表呼叫時分配給實例,ST22記錄的是無上下文分配的垃圾記憶體。使用CATCH後,在%CX_ROOT->GET_TEXT( )的文字中能找到引數。- 對於
DATA: gt_dyn TYPE TABLE OF” 等換行宣告,LOOP若編譯時類別未指定(全型別),短轉儲一定會出現** — 原因:動態內表不確定索引結構 ⇒ 必須用CAST到已知類型。**使用TRY / CATCH” 捕捉CX_SY_ITAB_LINE_ERROR。 - Explorer 無法從
ST22轉到ABAP_SOURCE∆ ⇒轉儲對應到封裝類的方法時,METHOD_NAME = 'IF_EX_BADI~EXECUTE'不會記到。SELECT ... SINGLE ... FOR ALL ENTRIES在出現重複鍵時短轉儲 — 鍵重複觸發CX_SY_OPEN_SQL_DB錯誤。始終SORT BY key_fields DESCENDING再DELETE ADJACENT DUPLICATES。 SE30分析SQL_TRACE中使用BYPASSING BUFFER時導致NEXT索引覆蓋問題 —BYPASSING BUFFER+FOR ALL ENTRIES不等於FOR ALL ENTRIES IN c。BYPASSING BUFFER直接到資料庫讀取最新數值,不受全域性緩衝影響。CALL TRANSACTION ... USING bdcdata的第 1 步不使用F(即必須是O) —BDC_OKCODE = '=XXXX'視作O。BDC_OKCODE = 'FC'等於不送ENTER,不觸發 PBO ⇒ 後續所有回寫皆不成功。
10. 症狀速查
| 症狀 | 分節 | 重點條目 |
|---|---|---|
| 激活返回 200 但資料沒生效 | §1 | HTTP 200 ≠ 成功;假成功最難發現;文字元素為獨立物件 |
| 報錯文案明顯不對 | §0 元規則 1 | 段序/關鍵字順序/型別不匹配優先 |
| 返回 S 但資料沒變或錯 | §3(尤其是 §3.2) | BAPI x 結構成對;SD 增強必用 CHAR1;BAPIPAREX 四段;借貸項看 TVEPZ |
| 編譯通過但激活或執行時報錯 | §2 | FORM 四禁忌;CALL FUNCTION 段序;INSERT REPORT STATE 順序;多欄位 SELECT 逗號 |
| 執行錯誤文案指向無關位置 | §0 元規則 1 | ABAP 編譯器報錯點是解析位置 |
| ALV 列標題空白或文字消失 | §3.3 | 欄位匿名內建類型;文字元素未激活;MESSAGE WITH 截斷 |
| 彈出框確認後無反應 | §5 | POPUP_GET_VALUES 的 RETURNCODE 反直覺 |
| F4 不出現或內容錯誤 | §5 | 域無值集;FIELD_TAB 推不出 INTTYPE;RS_VARIANT_VALUES 過載 |
| 鎖錯誤(假鎖) | §1 | 漏傳 corrNr;400 誤判為鎖;集團不對;ADT LOCK Accept 頭 |
| 資料重複或莫名丟失 | §3.1 | DELETE ADJACENT DUPLICATES 非全鍵;APPEND 覆蓋 sy-tabix;內聯 IF 分支 |
| 兩個介面跑結果不一致 | §4 | 獨立提交需要回讀;SD_SALESDOCUMENT_CHANGE UPDATE 佇列 |
| 無法寫入 FI 憑證 | §3.2, §1.1 | UMSKZ 無欄位;BAPIACHE09 缺匯率;OBJ_KEY 年度偏移 |
| 批次輸入/BDC 不執行 | §9 | CALL TRANSACTION 第一步需 O |
| OData/閘道返回 200 但資料為空 | §6 | 名稱大寫;max_hits 型別;KEY 小於 16 字元;MP 實體 increase |
11. 開工 Checklist(新專案)
按時間順序執行,每步驗證完成再進下一步。
11.1 摸底階段 (Day 1)
【目標:弄清你在哪個環境、對接什麼人、用什麼工具,別踩進第一天的坑。】
- 取得專案總綱(
sap-abap-project-ops或類似儲存庫):環境地址列表、請求號分配規則、物件命名約定。 - 確認 MCP 伺服器配置:(路徑、使用者、密碼、用戶端)。確保
NO_PROXY=*生效,本機無*_proxy環境變數。 - 確認 ADT 連通性:
get_programs/get_domains能否正常返回資料。 - 確認 ST22 許可權:用
ST22能查轉儲,而不是被許可權拒絕。 - 存放專案專用 Backup 目錄(
backup/),內含初始源碼快照。 - 建立初始
頻道.txt頻道日記:日期、工作任務、聯絡人。
11.2 動手前 (Day 1-2)
【目標:做好防護,避免直接傷害生產資料。】
- 通讀 §0 十條鐵律。確認理解每條的含義。
- 給 MCP 加上
process.exitCode = 1/ 統一finally語句:防止意外鎖殘留。 - 確認所有寫入命令都有
proxy: false。 - 制定文字元素推流方案:尤其對多語言專案,明確
ReadTextElements+ JSON diff 流程。 - 設定來源碼備份策略:每次改動前,先
get_source落庫。
11.3 編碼自檢
【目標:讓程式碼一次過編譯、避免靜默失效。】
- 所有
SELECT語句使用@宿主變數(包括CONSTANTS)。 - 所有
CALL FUNCTION按EXPORTING → IMPORTING → CHANGING → TABLES → EXCEPTIONS順序。 - 所有
FORM參數不寫TYPE c LENGTH n,用TYPE char<n>或 DDIC 類型。 - 所有
FORM的TABLES段寫在USING/CHANGING之前。 - 所有 BAPI
*X結構與主結構成對出現。 - 所有
LEFT JOIN處的右表過濾條件移進ON。 -
APPEND後不使用sy-tabix作後續索引。 -
DELETE ADJACENT DUPLICATES使用全鍵COMPARING。 - 內聯
DATA(...)不在IF/CASE分支裡宣告。 - 所有
READ TABLE ... WITH KEY檢查返回值。
11.4 交付四步
【目標:確保交付物完全生效,無丟失。】
- 生成:啟動所有交付物,若使用
CORR_NR,保證使用合法請求號。 - 推原始碼:
push_programs/push_includespush_domains等。 - 推文字元素:確認
PROG/PX單獨激活;push_textelements若無激活則須補啟動。 - CUA / GUI 狀態生成:
RS_CUA_INTERNAL_FETCH+_WRITE,確認state='A'。 - 驗證交付:回讀源碼與本地 diff 完全一致;運行功能測試確認業務正確。
11.5 專案收尾 (Day N)
【目標:清理測試殘留、歸檔知識、不留後門。】
- 刪除所有測試/探針物件:
$TMP、ZTEST_*等,確保TADIR清零。 - 確認所有 MCP 憑據、密鑰已清除或輪換:不留在交付物或備份中。
- 歸檔交付報告:
casebook.md若有新經驗,附加至新記錄。 - 刪除本地
backup/目錄(若安全)或歸檔至專案儲存庫。 - 向專案總綱提交本輪變更摘要:改動了哪些物件、修復了哪些問題、新增了哪些經驗。
12. 版本歷史
- v1.0: 初始版本,從
sap-abap-project-ops中沉澱的 120+ 條經驗。