:業(yè)務(wù)校驗(yàn)與數(shù)據(jù)填充實(shí)戰(zhàn)指南)
1. 項(xiàng)目概述為什么要在VA02保存前“動(dòng)手腳”做SAP SD模塊開(kāi)發(fā)或者運(yùn)維的朋友對(duì)VA02這個(gè)事務(wù)碼肯定再熟悉不過(guò)了。它就是銷(xiāo)售訂單修改的“主戰(zhàn)場(chǎng)”。日常業(yè)務(wù)中銷(xiāo)售訂單創(chuàng)建后客戶要求變更價(jià)格、調(diào)整數(shù)量、修改交貨日期甚至是增加一個(gè)全新的行項(xiàng)目這些操作最終都會(huì)匯集到VA02這個(gè)界面。從技術(shù)角度看VA02的保存操作點(diǎn)擊那個(gè)綠色的對(duì)勾或者按CtrlS是整個(gè)訂單數(shù)據(jù)從用戶界面流向SAP數(shù)據(jù)庫(kù)的最終閘口。一旦數(shù)據(jù)保存再想修改就需要走正式的修改流程甚至可能觸發(fā)后續(xù)的發(fā)貨、開(kāi)票等一連串操作。那么為什么我們常常需要在“保存前”這個(gè)關(guān)鍵時(shí)刻進(jìn)行增強(qiáng)呢核心驅(qū)動(dòng)力是業(yè)務(wù)規(guī)則的強(qiáng)制校驗(yàn)與數(shù)據(jù)的智能完善。SAP標(biāo)準(zhǔn)功能雖然強(qiáng)大但無(wú)法覆蓋所有企業(yè)千差萬(wàn)別的個(gè)性化需求。舉個(gè)例子你們公司規(guī)定對(duì)于特定銷(xiāo)售渠道比如電商平臺(tái)的訂單必須強(qiáng)制填寫(xiě)一個(gè)“平臺(tái)訂單號(hào)”或者對(duì)于金額超過(guò)100萬(wàn)的訂單需要自動(dòng)檢查客戶的信用額度并給出預(yù)警再比如需要在保存前根據(jù)物料和工廠自動(dòng)從某個(gè)自定義表中抓取一個(gè)“內(nèi)部批次號(hào)”填充到訂單行項(xiàng)目的增強(qiáng)字段里。這些需求標(biāo)準(zhǔn)SAP都沒(méi)有怎么辦答案就是通過(guò)ABAP增強(qiáng)在數(shù)據(jù)即將寫(xiě)入數(shù)據(jù)庫(kù)的瞬間介入處理。這個(gè)“保存前”的時(shí)機(jī)非常精妙。太早了不行比如在屏幕的PBOProcess Before Output階段用戶可能還沒(méi)輸完數(shù)據(jù)太晚了更不行數(shù)據(jù)一旦入庫(kù)再修改就是另一回事了。只有在“保存前”這個(gè)點(diǎn)用戶所有輸入的數(shù)據(jù)都已準(zhǔn)備就緒但尚未進(jìn)行最終的數(shù)據(jù)庫(kù)更新UPDATE此時(shí)進(jìn)行校驗(yàn)、計(jì)算和填充既能確保規(guī)則的執(zhí)行又能保證數(shù)據(jù)的完整性和準(zhǔn)確性。我經(jīng)手的項(xiàng)目中超過(guò)七成的SD增強(qiáng)需求都集中在這個(gè)環(huán)節(jié)。它就像一道精心設(shè)計(jì)的安檢門(mén)所有數(shù)據(jù)必須通過(guò)它的檢查才能“登機(jī)”。2. 核心增強(qiáng)點(diǎn)解析不止一個(gè)USEREXIT提到VA02的增強(qiáng)很多人的第一反應(yīng)是USEREXIT_SAVE_DOCUMENT_PREPARE。這沒(méi)錯(cuò)它是一個(gè)非常經(jīng)典且強(qiáng)大的出口User Exit。但根據(jù)我的經(jīng)驗(yàn)只依賴(lài)這一個(gè)點(diǎn)是不夠的一個(gè)健壯的保存前增強(qiáng)方案往往需要根據(jù)校驗(yàn)邏輯的復(fù)雜度和數(shù)據(jù)需求在多個(gè)增強(qiáng)點(diǎn)之間進(jìn)行選擇和組合。2.1 增強(qiáng)點(diǎn)一USEREXIT_SAVE_DOCUMENT_PREPARE (MV45AFZZ)這是最常用、最直接的增強(qiáng)點(diǎn)。它位于標(biāo)準(zhǔn)程序SAPMV45A的包含程序MV45AFZZ中。當(dāng)你在VA02中按下保存鍵標(biāo)準(zhǔn)程序在進(jìn)行了初步的格式和必填項(xiàng)檢查后就會(huì)調(diào)用這個(gè)出口。它的核心特點(diǎn)是數(shù)據(jù)完備此時(shí)所有屏幕和表格控件中的數(shù)據(jù)都已經(jīng)傳遞到了ABAP程序內(nèi)部的工作區(qū)比如銷(xiāo)售訂單抬頭數(shù)據(jù)VBAK、行項(xiàng)目數(shù)據(jù)VBAP等。你可以直接對(duì)這些內(nèi)表進(jìn)行讀取和修改。時(shí)機(jī)靠前它在最終數(shù)據(jù)庫(kù)更新UPDATE VBAK, VBAP...之前執(zhí)行是進(jìn)行業(yè)務(wù)邏輯校驗(yàn)和字段默認(rèn)值填充的黃金位置。功能強(qiáng)大你可以在這里進(jìn)行復(fù)雜的計(jì)算調(diào)用自定義函數(shù)讀取其他模塊的數(shù)據(jù)如財(cái)務(wù)的信用數(shù)據(jù)、物料的批次特性甚至根據(jù)條件彈出警告WARNING或錯(cuò)誤ERROR消息來(lái)阻止保存。一個(gè)典型的應(yīng)用場(chǎng)景假設(shè)公司要求所有“Z001”銷(xiāo)售類(lèi)型的訂單行項(xiàng)目都必須填寫(xiě)一個(gè)自定義的“項(xiàng)目經(jīng)理”字段假設(shè)已增強(qiáng)到VBAP結(jié)構(gòu)字段為ZPM。你可以在USEREXIT_SAVE_DOCUMENT_PREPARE中這樣寫(xiě)DATA: lv_vbeln TYPE vbeln. LOOP AT xvbap ASSIGNING FIELD-SYMBOL(fs_vbap) WHERE auart Z001. IF fs_vbap-zpm IS INITIAL. MESSAGE e001(zsd_order) WITH fs_vbap-posnr INTO DATA(lv_msg). CALL FUNCTION MESSAGE_STORE EXPORTING arbgb ZSD_ORDER msgty E msgv1 fs_vbap-posnr. cv_error_in_save X. 設(shè)置錯(cuò)誤標(biāo)志 ENDIF. ENDLOOP.注意這里使用MESSAGE_STORE函數(shù)而非直接的MESSAGE e...語(yǔ)句是因?yàn)樵赨ser Exit中直接彈出消息可能會(huì)與標(biāo)準(zhǔn)程序的消息處理機(jī)制沖突。將錯(cuò)誤消息存儲(chǔ)起來(lái)標(biāo)準(zhǔn)程序會(huì)在后續(xù)統(tǒng)一處理并阻止保存。2.2 增強(qiáng)點(diǎn)二BADISALES_DOCUMENT 和 DLV_HEADER_SAVE對(duì)于更新的SAP版本ECC 6.0及以上S/4HANASAP更推薦使用業(yè)務(wù)附加項(xiàng)BADI進(jìn)行增強(qiáng)。它們更面向?qū)ο蠊芾砥饋?lái)也更清晰。BADISALES_DOCUMENT:這個(gè)BADI非常強(qiáng)大它定義了許多方法對(duì)應(yīng)銷(xiāo)售單據(jù)生命周期中的各個(gè)時(shí)點(diǎn)。其中與VA02保存前相關(guān)的主要是PREPARE_SAVE: 類(lèi)似于USEREXIT_SAVE_DOCUMENT_PREPARE在保存準(zhǔn)備階段調(diào)用。你可以在這里修改單據(jù)數(shù)據(jù)。CHECK_SAVE: 在保存前進(jìn)行最終檢查。這是進(jìn)行強(qiáng)制性校驗(yàn)的最后關(guān)卡。如果在此方法中拋出異常CX_SALES_DOCUMENT或設(shè)置錯(cuò)誤消息保存將被終止。優(yōu)勢(shì)BADI方法有明確的輸入/輸出參數(shù)接口比如CHANGING參數(shù)CS_SALES_DOCUMENT就包含了完整的訂單數(shù)據(jù)對(duì)象操作起來(lái)比直接操作內(nèi)表更結(jié)構(gòu)化也更安全。BADIDLV_HEADER_SAVE:這個(gè)BADI主要針對(duì)交貨單但在某些銷(xiāo)售訂單保存場(chǎng)景也會(huì)被觸發(fā)例如當(dāng)銷(xiāo)售訂單自動(dòng)創(chuàng)建交貨時(shí)。如果你的校驗(yàn)邏輯與后續(xù)交貨流程強(qiáng)相關(guān)可能需要關(guān)注這個(gè)BADI。選擇User Exit還是BADI我的經(jīng)驗(yàn)是優(yōu)先使用BADI。BADI是SAP主推的增強(qiáng)技術(shù)具有更好的向下兼容性和可管理性通過(guò)SE19可以清晰看到所有實(shí)現(xiàn)。對(duì)于新項(xiàng)目或新需求無(wú)腦選BADI。User Exit更多用于維護(hù)歷史遺留的增強(qiáng)代碼或者在BADI不滿足特定細(xì)微需求時(shí)作為補(bǔ)充。2.3 增強(qiáng)點(diǎn)三隱形的守衛(wèi)屏幕增強(qiáng)與校驗(yàn)除了上述程序級(jí)的增強(qiáng)還有一種在數(shù)據(jù)進(jìn)入程序之前就進(jìn)行攔截的方法屏幕字段校驗(yàn)。通過(guò)在訂單屏幕的字段上附加FIELD_MODULE或者在屏幕流邏輯的PROCESS AFTER INPUT (PAI)事件中編寫(xiě)校驗(yàn)代碼可以在用戶離開(kāi)某個(gè)字段或執(zhí)行某個(gè)功能時(shí)立即進(jìn)行檢查。它的適用場(chǎng)景即時(shí)反饋例如用戶輸入一個(gè)物料號(hào)需要立即檢查該物料在選定工廠下是否有庫(kù)存并在旁邊顯示紅燈或綠燈。這種體驗(yàn)比保存時(shí)才報(bào)錯(cuò)要好得多。簡(jiǎn)單邏輯校驗(yàn)邏輯僅依賴(lài)于本字段或少數(shù)幾個(gè)其他屏幕字段的值。局限性復(fù)雜的、需要訪問(wèn)大量數(shù)據(jù)庫(kù)表或進(jìn)行復(fù)雜計(jì)算的校驗(yàn)不適合放在屏幕校驗(yàn)里會(huì)影響界面響應(yīng)速度。這類(lèi)校驗(yàn)更適合放到PREPARE_SAVE或CHECK_SAVE中。實(shí)操心得一個(gè)完整的VA02保存前增強(qiáng)體系往往是分層級(jí)的。屏幕校驗(yàn)處理簡(jiǎn)單、即時(shí)、體驗(yàn)好的檢查PREPARE_SAVE(或User Exit)處理復(fù)雜的數(shù)據(jù)計(jì)算、默認(rèn)值填充和依賴(lài)多表數(shù)據(jù)的校驗(yàn)CHECK_SAVE則作為最后一道不可逾越的防線執(zhí)行那些一旦違反就必須阻止保存的“鐵律”。三者結(jié)合才能構(gòu)建出既用戶友好又堅(jiān)固可靠的業(yè)務(wù)控制邏輯。3. 從零到一實(shí)現(xiàn)一個(gè)VA02保存前增強(qiáng)光說(shuō)不練假把式。下面我以一個(gè)真實(shí)的業(yè)務(wù)需求為例帶你走一遍使用BADISALES_DOCUMENT實(shí)現(xiàn)增強(qiáng)的完整流程。需求是對(duì)于銷(xiāo)售類(lèi)型為‘ZOR’網(wǎng)上零售的訂單檢查其行項(xiàng)目的“承諾交貨日期”VBEP-ETENR不能晚于創(chuàng)建日期VBAK-AUDAT后的30天。3.1 第一步需求分析與設(shè)計(jì)首先別急著敲代碼。先分析數(shù)據(jù)來(lái)源需要訂單抬頭信息VBAK-AUDAT和行項(xiàng)目計(jì)劃行信息VBEP-ETENR。校驗(yàn)時(shí)機(jī)必須在保存前檢查因?yàn)橐坏┍4驽e(cuò)誤的日期可能觸發(fā)錯(cuò)誤的MRP或交貨計(jì)劃。增強(qiáng)點(diǎn)選擇這是一個(gè)強(qiáng)業(yè)務(wù)規(guī)則校驗(yàn)需要在所有數(shù)據(jù)就緒后執(zhí)行并且校驗(yàn)失敗必須阻止保存。因此選擇BADISALES_DOCUMENT的CHECK_SAVE方法是最合適的。錯(cuò)誤處理校驗(yàn)不通過(guò)時(shí)需要向用戶明確提示是哪一行項(xiàng)目違反了規(guī)則。3.2 第二步查找并實(shí)現(xiàn)BADI打開(kāi)事務(wù)碼SE19Business Add-In Builder。在“創(chuàng)建實(shí)施”區(qū)域輸入BADI名稱(chēng)SALES_DOCUMENT然后點(diǎn)擊“創(chuàng)建實(shí)施”。給你的實(shí)施起一個(gè)名字比如Z_SD_ORDER_CHECK_DELIVERY并填寫(xiě)描述。點(diǎn)擊“繼續(xù)”。在“接口”標(biāo)簽頁(yè)你會(huì)看到BADI定義的所有方法。雙擊CHECK_SAVE方法進(jìn)入代碼編輯區(qū)。3.3 第三步編寫(xiě)核心校驗(yàn)邏輯在CHECK_SAVE方法中系統(tǒng)會(huì)傳入一個(gè)IS_SALES_DOCUMENT參數(shù)它包含了所有要保存的銷(xiāo)售單據(jù)數(shù)據(jù)。我們需要從中提取抬頭和計(jì)劃行數(shù)據(jù)。METHOD if_ex_sales_document~check_save. DATA: lv_audat TYPE audat, lv_max_date TYPE d, lv_error_flag TYPE abap_bool VALUE abap_false. FIELD-SYMBOLS: fs_header TYPE vbakvb, fs_schedule TYPE vbepvb. 1. 獲取銷(xiāo)售訂單抬頭數(shù)據(jù) READ TABLE is_sales_document-sales_document_header ASSIGNING fs_header INDEX 1. IF sy-subrc 0 OR fs_header IS NOT ASSIGNED. RETURN. 沒(méi)有抬頭數(shù)據(jù)直接退出 ENDIF. 2. 僅對(duì)銷(xiāo)售類(lèi)型為ZOR的訂單進(jìn)行檢查 IF fs_header-auart ZOR. RETURN. ENDIF. 3. 計(jì)算最大允許交貨日期創(chuàng)建日期30天 lv_audat fs_header-audat. lv_max_date lv_audat 30. 4. 循環(huán)檢查所有計(jì)劃行 LOOP AT is_sales_document-sales_document_schedule ASSIGNING fs_schedule. 檢查承諾日期是否晚于最大允許日期 IF fs_schedule-etenr lv_max_date. 5. 準(zhǔn)備錯(cuò)誤消息 消息類(lèi)ZSD_ORDER中定義消息ID 002內(nèi)容如“行項(xiàng)目的計(jì)劃行交貨日期不能晚于” MESSAGE e002(zsd_order) WITH fs_schedule-posnr fs_schedule-etenr lv_max_date INTO DATA(lv_message_text). 6. 記錄錯(cuò)誤并設(shè)置標(biāo)志 CALL FUNCTION MESSAGE_STORE EXPORTING arbgb ZSD_ORDER msgty E msgv1 fs_schedule-posnr msgv2 fs_schedule-etenr msgv3 lv_max_date. lv_error_flag abap_true. ENDIF. ENDLOOP. 7. 如果存在任何錯(cuò)誤拋出異常以阻止保存 IF lv_error_flag abap_true. RAISE EXCEPTION TYPE cx_sales_document EXPORTING textid cx_sales_documentdocument_not_saved. ENDIF. ENDMETHOD.代碼關(guān)鍵點(diǎn)解析IS_SALES_DOCUMENT這是一個(gè)復(fù)雜的結(jié)構(gòu)包含了抬頭HEADER、行項(xiàng)目ITEMS、計(jì)劃行SCHEDULE等多個(gè)內(nèi)表。你需要像剝洋蔥一樣找到需要的數(shù)據(jù)層級(jí)。MESSAGE_STORE這是在BADI或User Exit中報(bào)告錯(cuò)誤的標(biāo)準(zhǔn)做法。你不能直接用MESSAGE e...因?yàn)槟菚?huì)中斷方法執(zhí)行并可能導(dǎo)致程序狀態(tài)混亂。MESSAGE_STORE將錯(cuò)誤信息緩存起來(lái)標(biāo)準(zhǔn)程序會(huì)在適當(dāng)?shù)臅r(shí)機(jī)統(tǒng)一顯示。RAISE EXCEPTION在CHECK_SAVE中這是阻止保存的“殺手锏”。拋出CX_SALES_DOCUMENT異常后標(biāo)準(zhǔn)保存流程會(huì)中止并且之前通過(guò)MESSAGE_STORE存儲(chǔ)的所有錯(cuò)誤消息都會(huì)在VA02界面上顯示給用戶。3.4 第四步激活與測(cè)試編寫(xiě)完代碼后點(diǎn)擊“激活”按鈕激活整個(gè)BADI實(shí)施。進(jìn)入VA02找一個(gè)銷(xiāo)售類(lèi)型為ZOR的現(xiàn)有訂單進(jìn)行修改。將某個(gè)行項(xiàng)目的計(jì)劃行交貨日期修改為超過(guò)創(chuàng)建日期30天的未來(lái)日期。點(diǎn)擊保存。此時(shí)你應(yīng)該會(huì)看到系統(tǒng)彈出錯(cuò)誤消息明確提示哪一行違反了規(guī)則并且保存操作被阻止。將日期改回合規(guī)范圍再次保存此時(shí)訂單應(yīng)能成功保存。踩坑提醒測(cè)試時(shí)務(wù)必注意數(shù)據(jù)的完整性。有時(shí)計(jì)劃行表VBEP可能因?yàn)榕渲迷驔](méi)有數(shù)據(jù)你的LOOP AT可能根本進(jìn)不去。因此在增強(qiáng)代碼中加入適當(dāng)?shù)腎F sy-subrc 0判斷和日志記錄比如用MESSAGE s...調(diào)試是非常好的習(xí)慣。4. 高級(jí)技巧與避坑指南掌握了基礎(chǔ)實(shí)現(xiàn)我們?cè)賮?lái)聊聊那些在官方文檔里找不到但能讓你代碼更健壯、維護(hù)性更高的實(shí)戰(zhàn)技巧。4.1 如何高效調(diào)試保存前增強(qiáng)調(diào)試增強(qiáng)點(diǎn)尤其是保存前的邏輯有其特殊性。你不能像調(diào)試普通報(bào)表一樣直接設(shè)斷點(diǎn)。方法一使用外部調(diào)試器/h這是最直接的方法。在VA02界面輸入/h回車(chē)激活調(diào)試然后執(zhí)行保存操作。程序會(huì)停在保存函數(shù)如SAVE_DOCUMENT的開(kāi)始處。你需要一步步執(zhí)行直到進(jìn)入你的增強(qiáng)代碼MV45AFZZ或你的BADI方法。缺點(diǎn)是步驟繁瑣容易跟丟。方法二使用ABAP斷點(diǎn)BREAK-POINT或日志在增強(qiáng)代碼的關(guān)鍵位置插入BREAK-POINT語(yǔ)句。當(dāng)代碼執(zhí)行到此處時(shí)只要有用戶執(zhí)行保存操作并且觸發(fā)了你的增強(qiáng)邏輯調(diào)試器就會(huì)自動(dòng)彈出。注意這會(huì)影響所有用戶僅限開(kāi)發(fā)或測(cè)試環(huán)境使用生產(chǎn)環(huán)境絕對(duì)禁止。 更安全的方式是寫(xiě)入應(yīng)用日志Application Log使用事務(wù)碼SLG1可以查看。在代碼里用BAL_*系列函數(shù)記錄關(guān)鍵變量的值這是排查生產(chǎn)問(wèn)題的不二法門(mén)。方法三使用“靜默”測(cè)試有時(shí)你只想看邏輯是否走通不想打斷用戶。可以在代碼里用MESSAGE s... TYPE I彈出信息窗口或者將關(guān)鍵變量賦值給一個(gè)全局的調(diào)試內(nèi)表然后寫(xiě)一個(gè)簡(jiǎn)單的報(bào)表來(lái)顯示這個(gè)內(nèi)表的內(nèi)容。4.2 處理增強(qiáng)中的“數(shù)據(jù)不一致”陷阱在USEREXIT_SAVE_DOCUMENT_PREPARE或BADI的PREPARE_SAVE中你操作的是程序的工作區(qū)數(shù)據(jù)如XVBAK,XVBAP。一個(gè)常見(jiàn)的陷阱是你以為修改了這些內(nèi)表數(shù)據(jù)就萬(wàn)事大吉但標(biāo)準(zhǔn)程序在后續(xù)可能還有自己的推導(dǎo)或覆蓋邏輯。避坑法則修改時(shí)機(jī)盡量在標(biāo)準(zhǔn)程序完成其核心推導(dǎo)邏輯之后再進(jìn)行你的數(shù)據(jù)填充。對(duì)于User Exit這通常就是USEREXIT_SAVE_DOCUMENT_PREPARE本身。對(duì)于BADIPREPARE_SAVE的時(shí)機(jī)通常也合適。字段確認(rèn)修改了某個(gè)字段后如果這個(gè)字段會(huì)影響其他字段比如修改了價(jià)格可能影響定價(jià)條件你需要確認(rèn)標(biāo)準(zhǔn)程序是否會(huì)重新計(jì)算。如果不確定有時(shí)需要主動(dòng)調(diào)用相應(yīng)的函數(shù)如PRICING來(lái)觸發(fā)更新。測(cè)試邊界案例重點(diǎn)測(cè)試數(shù)據(jù)從無(wú)到有、從有到無(wú)清空、異常值等情況。例如你根據(jù)條件自動(dòng)填充了一個(gè)增強(qiáng)字段當(dāng)條件不滿足時(shí)你是保留原值還是清空它這需要和業(yè)務(wù)部門(mén)明確規(guī)則。4.3 性能優(yōu)化別讓增強(qiáng)拖慢系統(tǒng)增強(qiáng)代碼會(huì)在每次保存時(shí)執(zhí)行。如果代碼效率低下會(huì)直接影響所有用戶的訂單處理速度。優(yōu)化要點(diǎn)減少數(shù)據(jù)庫(kù)查詢(xún)避免在循環(huán)內(nèi)部執(zhí)行SELECT SINGLE。如果需要對(duì)一批物料進(jìn)行檢查應(yīng)該先用SELECT ... FOR ALL ENTRIES IN ...將所需數(shù)據(jù)一次性讀到內(nèi)表中然后在循環(huán)內(nèi)用READ TABLE來(lái)查找。 反例性能差 LOOP AT xvbap ASSIGNING fs_vbap. SELECT SINGLE mtart FROM mara INTO lv_mtart WHERE matnr fs_vbap-matnr. ... 處理邏輯 ENDLOOP. 正例性能好 DATA: lt_mara TYPE TABLE OF mara, lt_matnr TYPE TABLE OF matnr. lt_matnr VALUE #( FOR ls_vbap IN xvbap ( ls_vbap-matnr ) ). SELECT matnr, mtart INTO TABLE lt_mara FROM mara FOR ALL ENTRIES IN lt_matnr WHERE matnr lt_matnr-table_line. SORT lt_mara BY matnr. LOOP AT xvbap ASSIGNING fs_vbap. READ TABLE lt_mara ASSIGNING FIELD-SYMBOL(fs_mara) WITH KEY matnr fs_vbap-matnr BINARY SEARCH. IF sy-subrc 0. ... 使用 fs_mara-mtart 進(jìn)行處理 ENDIF. ENDLOOP.善用緩存對(duì)于一些不常變化的配置數(shù)據(jù)如自定義的檢查規(guī)則表可以在程序開(kāi)始時(shí)將其讀入一個(gè)全局的、帶內(nèi)存ID的共享內(nèi)存對(duì)象或者使用CL_SHM_AREA訪問(wèn)共享內(nèi)存避免每次保存都重復(fù)讀取。邏輯簡(jiǎn)化評(píng)估你的校驗(yàn)邏輯是否必要。能否前置到屏幕校驗(yàn)?zāi)芊裢ㄟ^(guò)配置如條件技術(shù)、字段狀態(tài)實(shí)現(xiàn)代碼越少性能越好。4.4 增強(qiáng)的版本管理與傳輸BADI實(shí)施和User Exit修改都是需要傳輸?shù)腁BAP對(duì)象。務(wù)必將其納入你的正規(guī)開(kāi)發(fā)流程。包分配創(chuàng)建BADI實(shí)施或修改包含程序時(shí)將其分配到一個(gè)合適的開(kāi)發(fā)包Package中以便于傳輸管理。傳輸請(qǐng)求所有修改都必須記錄在傳輸請(qǐng)求Transport Request中。使用事務(wù)碼SE10管理你的傳輸請(qǐng)求。注釋與文檔在增強(qiáng)代碼的開(kāi)頭使用規(guī)范的注釋塊說(shuō)明增強(qiáng)目的、作者、日期、需求編號(hào)。復(fù)雜的邏輯需要添加行內(nèi)注釋。這能為后續(xù)維護(hù)節(jié)省大量時(shí)間。集中管理考慮使用事務(wù)碼SMOD或CMOD來(lái)管理User Exit項(xiàng)目這樣可以將多個(gè)相關(guān)的Exit打包便于查看和傳輸。對(duì)于BADISE19本身就是一個(gè)管理工具。5. 常見(jiàn)問(wèn)題排查與實(shí)戰(zhàn)案例即使設(shè)計(jì)得再完美增強(qiáng)上線后也難免遇到問(wèn)題。這里我總結(jié)幾個(gè)最常被問(wèn)到的“坑”及其解決方案。5.1 問(wèn)題一增強(qiáng)代碼不執(zhí)行癥狀在VA02保存時(shí)斷點(diǎn)沒(méi)觸發(fā)日志也沒(méi)記錄好像增強(qiáng)不存在一樣。排查步驟激活狀態(tài)首先檢查你的BADI實(shí)施或包含程序MV45AFZZ是否已激活。未激活的對(duì)象不會(huì)被執(zhí)行。過(guò)濾器值如果使用了BADI檢查實(shí)施是否有過(guò)濾器Filter。例如SALES_DOCUMENTBADI可以按銷(xiāo)售組織、分銷(xiāo)渠道等設(shè)置過(guò)濾器。確保當(dāng)前處理的訂單符合過(guò)濾條件。增強(qiáng)點(diǎn)錯(cuò)誤確認(rèn)你修改的確實(shí)是正確的增強(qiáng)點(diǎn)。VA02的保存邏輯可能因不同條件如單據(jù)類(lèi)型、項(xiàng)目類(lèi)別而略有不同確保你的增強(qiáng)點(diǎn)在所有路徑上都能被調(diào)用。可以嘗試在SAVE_DOCUMENT函數(shù)模塊的入口處設(shè)斷點(diǎn)回溯調(diào)用棧來(lái)確認(rèn)執(zhí)行路徑。命名空間確保你的User Exit函數(shù)或FORM名稱(chēng)完全正確包括前綴如EXIT_SAPLV60B_001。5.2 問(wèn)題二消息顯示了但訂單仍然保存成功癥狀系統(tǒng)彈出了你設(shè)置的錯(cuò)誤消息但用戶點(diǎn)擊回車(chē)后訂單居然保存成功了。根因這幾乎可以肯定是消息類(lèi)型用錯(cuò)了。在USEREXIT_SAVE_DOCUMENT_PREPARE中如果你使用MESSAGE e...語(yǔ)句它可能無(wú)法正確中斷保存流程。更常見(jiàn)的是你雖然調(diào)用了MESSAGE_STORE存儲(chǔ)了E類(lèi)型消息但沒(méi)有設(shè)置錯(cuò)誤標(biāo)志或拋出異常。解決方案在User Exit中存儲(chǔ)E類(lèi)型消息后必須將CV_ERROR_IN_SAVE參數(shù)設(shè)置為‘X’。在BADI的CHECK_SAVE方法中存儲(chǔ)E類(lèi)型消息后必須RAISE EXCEPTION。檢查你的消息類(lèi)型是E錯(cuò)誤而不是W警告或I信息。5.3 問(wèn)題三增強(qiáng)修改的數(shù)據(jù)被標(biāo)準(zhǔn)程序覆蓋了癥狀你在增強(qiáng)里給某個(gè)字段賦了值但保存后發(fā)現(xiàn)值變了或者沒(méi)了。排查時(shí)機(jī)太早你可能在標(biāo)準(zhǔn)程序推導(dǎo)該字段值之前就修改了它隨后標(biāo)準(zhǔn)程序的邏輯覆蓋了你的修改。嘗試將你的賦值邏輯移到更靠后的增強(qiáng)點(diǎn)或者研究標(biāo)準(zhǔn)程序?qū)υ撟侄蔚馁x值邏輯在哪里。字段是計(jì)算字段有些字段是只讀的由其他字段通過(guò)公式計(jì)算得出如凈值 數(shù)量 × 單價(jià) - 折扣。直接修改這種字段是無(wú)效的。你需要修改它的源字段。使用標(biāo)準(zhǔn)函數(shù)對(duì)于某些關(guān)鍵字段的修改可能需要調(diào)用SAP提供的標(biāo)準(zhǔn)函數(shù)或BAPI來(lái)確保數(shù)據(jù)一致性。例如修改價(jià)格不應(yīng)直接改VBAP-KWMENG而應(yīng)通過(guò)條件技術(shù)Pricing來(lái)更新。5.4 實(shí)戰(zhàn)案例動(dòng)態(tài)默認(rèn)值填充需求銷(xiāo)售訂單的行項(xiàng)目上有一個(gè)增強(qiáng)字段“Z_PRIORITY”優(yōu)先級(jí)。業(yè)務(wù)希望當(dāng)用戶選擇“快速交貨”的運(yùn)輸路線Route時(shí)自動(dòng)將該行項(xiàng)目的優(yōu)先級(jí)設(shè)為“高”‘H’否則為“中”‘M’。實(shí)現(xiàn)思路增強(qiáng)點(diǎn)選擇這個(gè)需求需要在數(shù)據(jù)進(jìn)入程序后、保存前根據(jù)已有數(shù)據(jù)運(yùn)輸路線計(jì)算并填充另一個(gè)字段。選擇USEREXIT_SAVE_DOCUMENT_PREPARE或BADI的PREPARE_SAVE方法都很合適。邏輯實(shí)現(xiàn) 在 PREPARE_SAVE 或 User Exit 中 LOOP AT ct_sales_document-sales_document_item ASSIGNING FIELD-SYMBOL(fs_item). 獲取該行項(xiàng)目的運(yùn)輸路線這里假設(shè)從計(jì)劃行獲取實(shí)際可能需根據(jù)配置確定來(lái)源 READ TABLE ct_sales_document-sales_document_schedule INTO DATA(ls_schedule) WITH KEY posnr fs_item-posnr. IF sy-subrc 0 AND ls_schedule-route ZFAST. 假設(shè)ZFAST是快速交貨路線 fs_item-z_priority H. 高優(yōu)先級(jí) ELSE. fs_item-z_priority M. 中優(yōu)先級(jí) ENDIF. ENDLOOP.注意事項(xiàng)這里有一個(gè)關(guān)鍵點(diǎn)即**“否則”邏輯**。當(dāng)路線不是‘ZFAST’時(shí)我們將其設(shè)為‘M’。但這里需要考慮用戶是否已經(jīng)手動(dòng)輸入了優(yōu)先級(jí)我們的自動(dòng)填充邏輯是否會(huì)覆蓋用戶的手工輸入這是一個(gè)典型的業(yè)務(wù)邏輯沖突。通常的解決方案是僅當(dāng)字段初始為空時(shí)才自動(dòng)填充如果用戶已經(jīng)手動(dòng)輸入了值則尊重用戶輸入不予覆蓋。代碼需要增加一個(gè)判斷IF fs_item-z_priority IS INITIAL. ... ENDIF.這個(gè)案例看似簡(jiǎn)單卻涵蓋了增強(qiáng)開(kāi)發(fā)中“數(shù)據(jù)來(lái)源判斷”、“業(yè)務(wù)邏輯沖突處理”等核心思想。每一次增強(qiáng)開(kāi)發(fā)都是一次與標(biāo)準(zhǔn)程序邏輯和業(yè)務(wù)實(shí)際需求的深度對(duì)話。理解標(biāo)準(zhǔn)吃透業(yè)務(wù)你的增強(qiáng)代碼才能既穩(wěn)固又靈活。