从ECCX系统同步SO单据到S4
&---------------------------------------------------------------------
*& REPORT ZREP_SALEORDER_PULL_FROM_ECC
&---------------------------------------------------------------------
*& 目的:透過 RFC 從 ECC 讀取銷售訂單明細及價格條件,
*& 並在 S/4HANA 本地系統建立相同的銷售訂單 (外部給號)。
&---------------------------------------------------------------------
REPORT zrep_saleorder_pull_from_ecc.
INCLUDE:
TABLES:sscrfields.
" --- 定義 ALV 顯示用的結果結構 ---
TYPES: BEGIN OF ty_alv_result,
vbeln_ecc TYPE vbeln_va,
vbeln_s4 TYPE vbeln_va,
status TYPE icon_d, " 處理狀態 (紅/綠燈)
type TYPE bapi_mtype, " 訊息類型
id TYPE symsgid, " 訊息類別
number TYPE symsgno, " 訊息號碼
message TYPE bapi_msg, " 訊息內文
log_no TYPE balognr,
log_msg_no TYPE balmnr,
message_v1 TYPE symsgv,
message_v2 TYPE symsgv,
message_v3 TYPE symsgv,
message_v4 TYPE symsgv,
END OF ty_alv_result.
DATA: lt_seltexts TYPE TABLE OF rsseltexts,
ls_seltext TYPE rsseltexts.
DATA: gt_alv_result TYPE TABLE OF ty_alv_result.
DATA gv_vkorg TYPE vkorg .
" --- 畫面設計 ---
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
PARAMETERS: p_rfc TYPE rfcdest OBLIGATORY DEFAULT 'S4_ECC_888'.
PARAMETERS: p_vbeln TYPE vbeln_va OBLIGATORY.
PARAMETERS: p_old AS CHECKBOX .
PARAMETERS: p_rej AS CHECKBOX DEFAULT 'X'.
SELECTION-SCREEN END OF BLOCK b1.
SELECTION-SCREEN FUNCTION KEY 1 .
" --- 全域變數 (接收 ECC BAPI 資料) ---
DATA: gt_ecc_headers_out TYPE TABLE OF bapisdhd,
gt_ecc_items_out TYPE TABLE OF bapisdit,
gt_ecc_schedules_out TYPE TABLE OF bapisdhedu,
gt_ecc_partners_out TYPE TABLE OF bapisdpart,
gt_ecc_business_out TYPE TABLE OF bapisdbusi,
gt_ecc_conditions_out TYPE TABLE OF bapisdcond. " <-- 新增:接收 ECC 價格條件
----------------------------------------------------------------------
-
INITIALIZATION
----------------------------------------------------------------------
INITIALIZATION.CLEAR lt_seltexts[].
" 准备要修改的文本信息
CLEAR ls_seltext.
ls_seltext-name = 'P_RFC'. " 必须大写,与参数名一致
ls_seltext-kind = 'P'. " P 代表 PARAMETERS, S 代表 SELECT-OPTIONS
ls_seltext-text = 'RFC目标'.
APPEND ls_seltext TO lt_seltexts.CLEAR ls_seltext.
ls_seltext-name = 'P_VBELN'.
ls_seltext-kind = 'P'.
ls_seltext-text = '销售订单号'.
APPEND ls_seltext TO lt_seltexts.CLEAR ls_seltext.
ls_seltext-name = 'P_OLD'.
ls_seltext-kind = 'P'.
ls_seltext-text = '补全单据:对已经存在的订单补全行项目'.
APPEND ls_seltext TO lt_seltexts.CLEAR ls_seltext.
ls_seltext-name = 'P_REJ'.
ls_seltext-kind = 'P'.
ls_seltext-text = '拒绝订单-对新同步单据有效'.
APPEND ls_seltext TO lt_seltexts." 调用功能模块更新当前程序的选择文本
CALL FUNCTION 'SELECTION_TEXTS_MODIFY'
EXPORTING
program = sy-repid
TABLES
seltexts = lt_seltexts
EXCEPTIONS
OTHERS = 1.IF sy-subrc <> 0.
" 错误处理
ENDIF.PERFORM frm_init_funckey .
AT SELECTION-SCREEN .
CASE sscrfields-ucomm.
WHEN 'FC01'.
PERFORM frm_view_maintence USING 'ZTSO_KUNNR' .
WHEN 'FC02'.
WHEN 'FC03'.
WHEN 'FC04'.
WHEN OTHERS.
ENDCASE.
----------------------------------------------------------------------
-
START-OF-SELECTION
----------------------------------------------------------------------
START-OF-SELECTION.SELECT COUNT(*) FROM vbak WHERE vbeln = p_vbeln.
IF sy-subrc = 0 .
IF p_old = '' .
MESSAGE |销售单订单{ p_vbeln }已经存在| TYPE 'S' DISPLAY LIKE 'E' .
LEAVE LIST-PROCESSING .
ENDIF .
ELSE.
IF p_old = 'X' .
MESSAGE |销售单订单{ p_vbeln }不经存在| TYPE 'S' DISPLAY LIKE 'E' .
LEAVE LIST-PROCESSING .
ENDIF .
ENDIF.PERFORM f_check_rfc.
PERFORM f_read_ecc_order.IF lines( gt_ecc_headers_out ) > 0.
IF p_old = 'X' .
PERFORM f_update_s4_order. "只补充S4缺少的行,如果行已经存在,忽略跳过
ELSE.
PERFORM f_create_s4_order.
ENDIF .
ENDIF .PERFORM f_display_salv.
----------------------------------------------------------------------
-
FORM: 檢查 RFC
----------------------------------------------------------------------
FORM f_check_rfc.
CALL FUNCTION 'RFC_PING'
DESTINATION p_rfc
EXCEPTIONS
communication_failure = 1
system_failure = 2.IF sy-subrc <> 0.
MESSAGE |RFC connection to { p_rfc } failed.| TYPE 'S' DISPLAY LIKE 'E'.
LEAVE LIST-PROCESSING .
ENDIF.
ENDFORM.
----------------------------------------------------------------------
-
FORM: 讀取 ECC 並建立 S/4
----------------------------------------------------------------------
FORM f_read_ecc_order.
DATA: lv_bapi_view TYPE order_view,
lt_sales_documents TYPE TABLE OF sales_key,
ls_sales_document TYPE sales_key,
lt_return_ecc TYPE TABLE OF bapiret2." 1. 設定查詢號碼
ls_sales_document-vbeln = p_vbeln.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = ls_sales_document-vbeln
IMPORTING
output = ls_sales_document-vbeln.
APPEND ls_sales_document TO lt_sales_documents." 2. 勾選要讀取的區塊,包含價格條件 (SDCOND)
CLEAR lv_bapi_view.
lv_bapi_view-header = 'X'.
lv_bapi_view-item = 'X'.
lv_bapi_view-sdschedule = 'X'.
lv_bapi_view-partner = 'X'.
lv_bapi_view-business = 'X'.
lv_bapi_view-sdcond = 'X'." 3. 呼叫 ECC BAPI
CALL FUNCTION 'BAPISDORDER_GETDETAILEDLIST'
DESTINATION p_rfc
EXPORTING
i_bapi_view = lv_bapi_view
TABLES
sales_documents = lt_sales_documents
order_headers_out = gt_ecc_headers_out
order_items_out = gt_ecc_items_out
order_schedules_out = gt_ecc_schedules_out
order_partners_out = gt_ecc_partners_out
order_business_out = gt_ecc_business_out
order_conditions_out = gt_ecc_conditions_out " <-- 新增:接收條件
return = lt_return_ecc." 4. 檢查讀取結果
READ TABLE lt_return_ecc TRANSPORTING NO FIELDS WITH KEY type = 'E'.
IF sy-subrc = 0 OR lines( gt_ecc_headers_out ) = 0.
PERFORM f_append_alv_log USING p_vbeln '' '@0A@' lt_return_ecc.
IF lines( lt_return_ecc ) = 0.
DATA(lt_dummy_err) = VALUE bapiret2_t( ( type = 'E' message = 'Order not found in ECC.' ) ).
PERFORM f_append_alv_log USING p_vbeln '' '@0A@' lt_dummy_err.
ENDIF.
RETURN.
ENDIF.
ENDFORM.
----------------------------------------------------------------------
-
FORM: 建立 S/4 訂單 (包含外部給號與價格條件)
----------------------------------------------------------------------
FORM f_create_s4_order.
DATA: ls_header_in TYPE bapisdhd1,
ls_header_inx TYPE bapisdhd1x,
ls_logic_switch TYPE bapisdls, " <-- 用於控制定價邏輯
lt_items_in TYPE TABLE OF bapisditm,
lt_items_inx TYPE TABLE OF bapisditmx,
lt_partners TYPE TABLE OF bapiparnr,
lt_schedules_in TYPE TABLE OF bapischdl,
lt_schedules_inx TYPE TABLE OF bapischdlx,
lt_conditions_in TYPE TABLE OF bapicond, " <-- 準備寫入的條件
lt_conditions_inx TYPE TABLE OF bapicondx, " <-- 條件控制標記
lt_return_s4 TYPE TABLE OF bapiret2.DATA: lv_s4_vbeln TYPE vbeln_va,
lv_ecc_vbeln TYPE vbeln_va,
lv_status_icon TYPE icon_d.
DATA lv_werks TYPE werks_d.SELECT * INTO TABLE @DATA(lt_kunnr) FROM ztso_kunnr .
SORT lt_kunnr BY his_kunnr." --- [A] 標頭映射 (外部給號) ---
READ TABLE gt_ecc_headers_out INTO DATA(ls_ecc_header) INDEX 1.
IF sy-subrc = 0.
ls_header_in-doc_type = ls_ecc_header-doc_type.
ls_header_in-sales_org = ls_ecc_header-sales_org.
ls_header_in-distr_chan = ls_ecc_header-distr_chan.
ls_header_in-division = ls_ecc_header-division.
ls_header_in-req_date_h = ls_ecc_header-req_date_h.
ls_header_in-purch_no_c = ls_ecc_header-purch_no.
ls_header_in-dlvschduse = ls_ecc_header-dlvschedus.ls_header_inx-updateflag = 'I'.
ls_header_inx-doc_type = 'X'.
ls_header_inx-sales_org = 'X'.
ls_header_inx-distr_chan = 'X'.
ls_header_inx-division = 'X'.
ls_header_inx-req_date_h = 'X'.
ls_header_inx-purch_no_c = 'X'.
ls_header_inx-dlvschduse = 'X'.
ENDIF." --- [B] 合作夥伴映射 ---
LOOP AT gt_ecc_partners_out INTO DATA(ls_ecc_partner).
APPEND INITIAL LINE TO lt_partners ASSIGNING FIELD-SYMBOL(<fs_partner>).
<fs_partner>-partn_role = ls_ecc_partner-partn_role.
<fs_partner>-partn_numb = ls_ecc_partner-customer.READ TABLE lt_kunnr INTO DATA(ls_kunnr)
WITH KEY his_kunnr = ls_ecc_partner-customer BINARY SEARCH .
IF sy-subrc = 0 AND ls_kunnr-new_kunnr IS NOT INITIAL .
<fs_partner>-partn_numb = ls_kunnr-new_kunnr .
ENDIF.ENDLOOP.
" --- [C] 項目映射 ---
LOOP AT gt_ecc_items_out INTO DATA(ls_ecc_item).APPEND INITIAL LINE TO lt_items_in ASSIGNING FIELD-SYMBOL(<fs_item_in>).
APPEND INITIAL LINE TO lt_items_inx ASSIGNING FIELD-SYMBOL(<fs_item_inx>).<fs_item_in>-itm_number = ls_ecc_item-itm_number.
<fs_item_in>-material = ls_ecc_item-material.
<fs_item_in>-po_itm_no = ls_ecc_item-po_itm_no .
<fs_item_in>-target_qty = ls_ecc_item-req_qty.
<fs_item_in>-cust_mat22 = ls_ecc_item-cust_mat22 .
<fs_item_in>-hg_lv_item = ls_ecc_item-hg_lv_item .
<fs_item_in>-reason_rej = ls_ecc_item-rea_for_re. "拒绝原因IF p_rej = 'X' .
<fs_item_in>-reason_rej = '00'. "新单据,如果勾选了拒绝订单,都默认拒绝原因 00
ENDIF .PERFORM frm_get_plant USING ls_ecc_item-plant CHANGING <fs_item_in>-plant.
lv_werks = <fs_item_in>-plant .<fs_item_inx>-updateflag = 'I'.
<fs_item_inx>-itm_number = ls_ecc_item-itm_number.
<fs_item_inx>-material = 'X'.
<fs_item_inx>-reason_rej = 'X'.
<fs_item_inx>-po_itm_no = 'X'.
<fs_item_inx>-target_qty = 'X'.
<fs_item_inx>-cust_mat22 = 'X'.
<fs_item_inx>-hg_lv_item = 'X'.
<fs_item_inx>-plant = 'X'." --- 處理項目層級的業務資料 (如果在前面暫存了) ---
" 尋找對應的項目業務資料
READ TABLE gt_ecc_business_out INTO DATA(ls_item_business)
WITH KEY itm_number = ls_ecc_item-itm_number.
IF sy-subrc = 0.
<fs_item_in>-incoterms1 = ls_item_business-incoterms1.
<fs_item_inx>-incoterms1 = 'X'.
ENDIF.READ TABLE gt_ecc_business_out INTO DATA(ls_ecc_business)
WITH KEY itm_number = ls_ecc_item-itm_number.
IF sy-subrc = 0.
<fs_item_in>-ref_1 = ls_ecc_business-ref_1 .
<fs_item_in>-purch_no_s = ls_ecc_business-purch_no_s .
<fs_item_in>-ref_1_s = ls_ecc_business-ref_1_s .
<fs_item_in>-purch_no_c = ls_ecc_business-purch_no_c .<fs_item_inx>-ref_1 = 'X'.
<fs_item_inx>-purch_no_s = 'X'.
<fs_item_inx>-ref_1_s = 'X'.
<fs_item_inx>-purch_no_c = 'X'.
ENDIF.ENDLOOP.
" --- [D] 排程線映射 ---
LOOP AT gt_ecc_schedules_out INTO DATA(ls_ecc_schedule).
APPEND INITIAL LINE TO lt_schedules_in ASSIGNING FIELD-SYMBOL(<fs_sched_in>).
APPEND INITIAL LINE TO lt_schedules_inx ASSIGNING FIELD-SYMBOL(<fs_sched_inx>).<fs_sched_in>-itm_number = ls_ecc_schedule-itm_number.
<fs_sched_in>-sched_line = ls_ecc_schedule-sched_line.
<fs_sched_in>-req_qty = ls_ecc_schedule-req_qty.
<fs_sched_in>-req_date = ls_ecc_schedule-req_date.<fs_sched_inx>-updateflag = 'I'.
<fs_sched_inx>-itm_number = ls_ecc_schedule-itm_number.
<fs_sched_inx>-sched_line = ls_ecc_schedule-sched_line.
<fs_sched_inx>-req_qty = 'X'.
<fs_sched_inx>-req_date = 'X'.
ENDLOOP." --- [E] 價格條件映射 ---
" 為了避免帶入一些系統自動算出來(無法手動輸入)的條件,
" 建議在此處過濾掉一些統計型或稅金條件(視業務需求而定)。
" 這裡我們示範帶入所有條件。
LOOP AT gt_ecc_conditions_out INTO DATA(ls_ecc_cond).
IF ls_ecc_cond-condorigin = 'A' OR
ls_ecc_cond-condorigin = '' .
CONTINUE .
ENDIF.
" 如果需要過濾條件類型,可以在這裡寫 CHECK ls_ecc_cond-cond_type = 'PR00'. 之類的邏輯APPEND INITIAL LINE TO lt_conditions_in ASSIGNING FIELD-SYMBOL(<fs_cond_in>).
APPEND INITIAL LINE TO lt_conditions_inx ASSIGNING FIELD-SYMBOL(<fs_cond_inx>)." 條件的基本鍵值
<fs_cond_in>-itm_number = ls_ecc_cond-itm_number.
<fs_cond_in>-cond_st_no = ls_ecc_cond-cond_st_no. " 條件階層號碼
<fs_cond_in>-cond_count = ls_ecc_cond-cond_count. " 條件計數器
<fs_cond_in>-cond_type = ls_ecc_cond-cond_type. " 條件類型 (如 PR00)
<fs_cond_in>-cond_value = ls_ecc_cond-cond_value. " 條件金額
<fs_cond_in>-currency = ls_ecc_cond-currency. " 幣別
<fs_cond_in>-cond_unit = ls_ecc_cond-cond_d_unt. " 單位<fs_cond_in>-cond_p_unt = ls_ecc_cond-cond_p_unt. " 價格單位 (per x unit)
" 更新標記
<fs_cond_inx>-updateflag = 'I'.
<fs_cond_inx>-itm_number = ls_ecc_cond-itm_number.
<fs_cond_inx>-cond_st_no = ls_ecc_cond-cond_st_no.
<fs_cond_inx>-cond_count = ls_ecc_cond-cond_count.
<fs_cond_inx>-cond_type = ls_ecc_cond-cond_type.
<fs_cond_inx>-cond_value = 'X'.
<fs_cond_inx>-currency = 'X'.
<fs_cond_inx>-cond_unit = 'X'.
<fs_cond_inx>-cond_p_unt = 'X'.
ENDLOOP.SELECT * FROM t171 INTO TABLE @DATA(lt_t171) .
SELECT * FROM t189 INTO TABLE @DATA(lt_t189) .
SELECT * FROM t188 INTO TABLE @DATA(lt_t188) .LOOP AT gt_ecc_business_out INTO ls_ecc_business WHERE itm_number = '000000'.
" 表頭層級業務資料
ls_header_in-incoterms1 = ls_ecc_business-incoterms1.
ls_header_in-incoterms2 = ls_ecc_business-incoterms2.
ls_header_in-price_date = ls_ecc_business-price_date.READ TABLE lt_t188 TRANSPORTING NO FIELDS WITH KEY
konda = ls_ecc_business-price_grp .
IF sy-subrc = 0.
ls_header_in-price_grp = ls_ecc_business-price_grp.
ENDIF.READ TABLE lt_t189 TRANSPORTING NO FIELDS WITH KEY
pltyp = ls_ecc_business-price_list .
IF sy-subrc = 0.
ls_header_in-price_list = ls_ecc_business-price_list.
ENDIF.READ TABLE lt_t171 TRANSPORTING NO FIELDS WITH KEY
bzirk = ls_ecc_business-sales_dist .
IF sy-subrc = 0.
ls_header_in-sales_dist = ls_ecc_business-sales_dist.
ENDIF." 標記更新
IF ls_ecc_business-incoterms1 IS NOT INITIAL. ls_header_inx-incoterms1 = 'X'. ENDIF.
IF ls_ecc_business-incoterms2 IS NOT INITIAL. ls_header_inx-incoterms2 = 'X'. ENDIF.
IF ls_ecc_business-price_grp IS NOT INITIAL. ls_header_inx-price_grp = 'X'. ENDIF.
IF ls_ecc_business-price_date IS NOT INITIAL. ls_header_inx-price_date = 'X'. ENDIF.
IF ls_ecc_business-price_list IS NOT INITIAL. ls_header_inx-price_list = 'X'. ENDIF.
IF ls_ecc_business-sales_dist IS NOT INITIAL. ls_header_inx-sales_dist = 'X'. ENDIF.ENDLOOP.
" 判断原单的公司代码与工厂对应的公司代码是否一致,如果不一致,更改销售组织
CLEAR gv_vkorg .
SELECT SINGLE bukrs INTO @DATA(lv_source_bukrs) "原公司
FROM tvko
WHERE vkorg = @ls_header_in-sales_org.IF lv_werks IS NOT INITIAL . "查找公司对应的新公司
SELECT SINGLE bukrs INTO @DATA(lv_target_bukrs)
FROM t001k AS a
INNER JOIN t001w AS b ON a~bwkey = b~bwkey
WHERE b~werks = @lv_werks.
ENDIF .IF lv_target_bukrs <> lv_source_bukrs.
SELECT SINGLE vkorg INTO gv_vkorg "新的销售组织
FROM tvko
WHERE bukrs = lv_target_bukrs.
IF sy-subrc = 0 .
ls_header_in-sales_org = gv_vkorg .
ENDIF .
ENDIF." 【重要】控制定價重算邏輯 (Pricing Logic Switch)
" 如果你不設定,S/4 可能會根據當下的主檔重新計算價格,覆蓋你帶進來的條件。
" 'G': 不改變定價元素,只複製無變更的手動輸入價格。這是遷移舊單常用的設定。
ls_logic_switch-pricing = ' '.
lv_ecc_vbeln = p_vbeln. " 預期單號" --- [F] 呼叫 S/4 BAPI 建立訂單 ---
CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2'
EXPORTING
salesdocumentin = lv_ecc_vbeln
order_header_in = ls_header_in
order_header_inx = ls_header_inx
logic_switch = ls_logic_switch " <-- 帶入邏輯開關
IMPORTING
salesdocument = lv_s4_vbeln
TABLES
return = lt_return_s4
order_items_in = lt_items_in
order_items_inx = lt_items_inx
order_partners = lt_partners
order_schedules_in = lt_schedules_in
order_schedules_inx = lt_schedules_inx
order_conditions_in = lt_conditions_in " <-- 帶入條件
order_conditions_inx = lt_conditions_inx. " <-- 帶入條件標記" --- [G] 結果檢查 ---
READ TABLE lt_return_s4 TRANSPORTING NO FIELDS WITH KEY type = 'E'.
IF sy-subrc <> 0.
READ TABLE lt_return_s4 TRANSPORTING NO FIELDS WITH KEY type = 'A'.
ENDIF.IF sy-subrc <> 0.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'.
lv_status_icon = '@08@'.READ TABLE lt_return_s4 TRANSPORTING NO FIELDS WITH KEY type = 'S'.
IF sy-subrc <> 0.
APPEND VALUE bapiret2( type = 'S' message = |Sales Order { lv_s4_vbeln } created via external numbering.| ) TO lt_return_s4.
ENDIF.IF gv_vkorg IS NOT INITIAL .
UPDATE ztktsd001 SET vkorg = gv_vkorg WHERE vbeln = lv_s4_vbeln . -
UPDATE ztktsd002 SET vkorg = gv_vkorg WHERE vbeln = lv_new_vbeln .
COMMIT WORK .
ENDIF .
ELSE. -
IF lv_s4_vbeln <> p_vbeln.
-
APPEND VALUE bapiret2( type = 'S' message = |Sales Order { lv_s4_vbeln } created via external numbering.| ) TO lt_return_s4.
-
ENDIF.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
lv_status_icon = '@0A@'.
ENDIF.
PERFORM f_append_alv_log USING p_vbeln lv_s4_vbeln lv_status_icon lt_return_s4.
ENDFORM.
&---------------------------------------------------------------------
*& Form FRM_VIEW_MAINTENCE
&---------------------------------------------------------------------
-
text
---------------------------------------------------------------------- -
-->P_0480 text
----------------------------------------------------------------------
FORM frm_view_maintence USING uv_view_name .
DATA lv_view_name LIKE dd02v-tabname .lv_view_name = uv_view_name .
CALL FUNCTION 'VIEW_MAINTENANCE_CALL'
EXPORTING
action = 'U'
view_name = lv_view_name
EXCEPTIONS
client_reference = 1
foreign_lock = 2
invalid_action = 3
no_clientindependent_auth = 4
no_database_function = 5
no_editor_function = 6
no_show_auth = 7
no_tvdir_entry = 8
no_upd_auth = 9
only_show_allowed = 10
system_failure = 11
unknown_field_in_dba_sellist = 12
view_not_found = 13
maintenance_prohibited = 14
OTHERS = 15.
IF sy-subrc <> 0. -
Implement suitable error handling here
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
ENDFORM. " FRM_VIEW_MANTENCE
----------------------------------------------------------------------
-
FORM: 將 BAPI Return 表轉換至 ALV 內表
----------------------------------------------------------------------
FORM f_append_alv_log USING pv_vbeln_ecc TYPE vbeln_va
pv_vbeln_s4 TYPE vbeln_va
pv_icon TYPE icon_d
pt_return TYPE bapiret2_t.
DATA: ls_alv TYPE ty_alv_result.LOOP AT pt_return INTO DATA(ls_return).
CLEAR ls_alv.
ls_alv-vbeln_ecc = pv_vbeln_ecc.
ls_alv-vbeln_s4 = pv_vbeln_s4.
ls_alv-status = pv_icon.ls_alv-type = ls_return-type.
ls_alv-id = ls_return-id.
ls_alv-number = ls_return-number.
ls_alv-message = ls_return-message.
ls_alv-log_no = ls_return-log_no.
ls_alv-log_msg_no = ls_return-log_msg_no.
ls_alv-message_v1 = ls_return-message_v1.
ls_alv-message_v2 = ls_return-message_v2.
ls_alv-message_v3 = ls_return-message_v3.
ls_alv-message_v4 = ls_return-message_v4.APPEND ls_alv TO gt_alv_result.
ENDLOOP.
ENDFORM.
----------------------------------------------------------------------
-
FORM: 使用 CL_SALV_TABLE 顯示結果
----------------------------------------------------------------------
FORM f_display_salv.
DATA: lo_alv TYPE REF TO cl_salv_table,
lo_functions TYPE REF TO cl_salv_functions_list,
lo_columns TYPE REF TO cl_salv_columns_table,
lo_column TYPE REF TO cl_salv_column_table." 如果內表為空,不顯示 ALV
IF lines( gt_alv_result ) = 0.
WRITE: / 'No log entries found.'.
RETURN.
ENDIF.TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = lo_alv
CHANGING
t_table = gt_alv_result )." 啟用所有標準工具列按鈕
lo_functions = lo_alv->get_functions( ).
lo_functions->set_all( abap_true )." 最佳化欄寬並設定標題
lo_columns = lo_alv->get_columns( ).
lo_columns->set_optimize( abap_true ).TRY.
" 設定 STATUS 欄位為圖示
lo_column ?= lo_columns->get_column( 'STATUS' ).
lo_column->set_short_text( 'Status' ).
lo_column->set_medium_text( 'Status' ).
lo_column->set_icon( abap_true ).lo_column ?= lo_columns->get_column( 'VBELN_ECC' ).
lo_column->set_short_text( 'ECC SO' ).
lo_column->set_medium_text( 'ECC Order No.' ).lo_column ?= lo_columns->get_column( 'VBELN_S4' ).
lo_column->set_short_text( 'S/4 SO' ).
lo_column->set_medium_text( 'S/4 Order No.' ).
CATCH cx_salv_not_found.
" Ignore
ENDTRY.lo_alv->display( ).
MESSAGE |销售订单的销售组织更新为{ gv_vkorg }| TYPE 'S' .CATCH cx_salv_msg.
MESSAGE 'Error creating ALV grid' TYPE 'E'.
ENDTRY.
ENDFORM.
&---------------------------------------------------------------------
*& Form frm_init_funckey
&---------------------------------------------------------------------
*& text
&---------------------------------------------------------------------
*& --> p1 text
*& <-- p2 text
&---------------------------------------------------------------------
FORM frm_init_funckey .
DATA:ls_functxt TYPE smp_dyntxt .
CLEAR ls_functxt .
ls_functxt-icon_id = '@KE@' .
ls_functxt-icon_text = '客户转换对照表'.
ls_functxt-quickinfo = '客户转换对照表'.
sscrfields-functxt_01 = ls_functxt .
ENDFORM.
&---------------------------------------------------------------------
*& Form f_update_s4_order
&---------------------------------------------------------------------
*& 只補充 S/4 缺少的行,如果行已經存在,則忽略跳過
&---------------------------------------------------------------------
FORM f_update_s4_order.
" 宣告 BAPI 參數
DATA: ls_header_in TYPE bapisdh1, " 新增: 空的 Header_in 結構
ls_header_inx TYPE bapisdh1x,
ls_logic_switch TYPE bapisdls,
lt_items_in TYPE TABLE OF bapisditm,
lt_items_inx TYPE TABLE OF bapisditmx,
lt_schedules_in TYPE TABLE OF bapischdl,
lt_schedules_inx TYPE TABLE OF bapischdlx,
lt_conditions_in TYPE TABLE OF bapicond,
lt_conditions_inx TYPE TABLE OF bapicondx,
lt_return_s4 TYPE TABLE OF bapiret2.
DATA: lv_s4_vbeln TYPE vbeln_va,
lv_status_icon TYPE icon_d,
lv_has_new_items TYPE abap_bool,
lv_werks TYPE werks_d.
" 1. 取得 S/4 本地系統目前該訂單已存在的項目 (Item)
SELECT posnr
INTO TABLE @DATA(lt_s4_items)
FROM vbap
WHERE vbeln = @p_vbeln.
SORT lt_s4_items BY posnr.
" 設定更新的 Header 標記 (必須設定 updateflag = 'U' 表示要修改這張訂單)
CLEAR ls_header_in.
CLEAR ls_header_inx.
ls_header_inx-updateflag = 'U'.
" 【重要】控制定價重算邏輯 (與 Create 相同,避免改變舊單定價)
CLEAR ls_logic_switch.
ls_logic_switch-pricing = ' '.
lv_has_new_items = abap_false.
" 2. 處理項目映射 (Item)
LOOP AT gt_ecc_items_out INTO DATA(ls_ecc_item).
" 檢查該項目是否已存在於 S/4
READ TABLE lt_s4_items TRANSPORTING NO FIELDS
WITH KEY posnr = ls_ecc_item-itm_number BINARY SEARCH.
IF sy-subrc = 0.
" 項目已存在,跳過
CONTINUE.
ENDIF.
" 標記有新項目需要新增
lv_has_new_items = abap_true.
" 準備新增項目資料
APPEND INITIAL LINE TO lt_items_in ASSIGNING FIELD-SYMBOL(<fs_item_in>).
APPEND INITIAL LINE TO lt_items_inx ASSIGNING FIELD-SYMBOL(<fs_item_inx>).
<fs_item_in>-itm_number = ls_ecc_item-itm_number.
<fs_item_in>-material = ls_ecc_item-material.
<fs_item_in>-po_itm_no = ls_ecc_item-po_itm_no .
<fs_item_in>-target_qty = ls_ecc_item-req_qty.
<fs_item_in>-cust_mat22 = ls_ecc_item-cust_mat22 .
<fs_item_in>-hg_lv_item = ls_ecc_item-hg_lv_item .
<fs_item_in>-reason_rej = '00'. "默认拒绝原因 00
PERFORM frm_get_plant USING ls_ecc_item-plant CHANGING <fs_item_in>-plant.
lv_werks = <fs_item_in>-plant.
<fs_item_inx>-updateflag = 'I'. " 【關鍵】對於現有訂單新增行,必須使用 'I'
<fs_item_inx>-itm_number = ls_ecc_item-itm_number.
<fs_item_inx>-material = 'X'.
<fs_item_inx>-po_itm_no = ls_ecc_item-po_itm_no .
<fs_item_inx>-target_qty = ls_ecc_item-req_qty.
<fs_item_inx>-cust_mat22 = ls_ecc_item-cust_mat22 .
<fs_item_inx>-plant = 'X'.
<fs_item_inx>-hg_lv_item = 'X'.
<fs_item_inx>-reason_rej = 'X'.
" 處理項目層級的業務資料 (Incoterms 等)
READ TABLE gt_ecc_business_out INTO DATA(ls_item_business)
WITH KEY itm_number = ls_ecc_item-itm_number.
IF sy-subrc = 0.
<fs_item_in>-incoterms1 = ls_item_business-incoterms1.
<fs_item_inx>-incoterms1 = 'X'.
ENDIF.
ENDLOOP.
READ TABLE gt_ecc_business_out INTO DATA(ls_ecc_business)
WITH KEY itm_number = ls_ecc_item-itm_number.
IF sy-subrc = 0.
<fs_item_in>-ref_1 = ls_ecc_business-ref_1 .
<fs_item_in>-purch_no_s = ls_ecc_business-purch_no_s .
<fs_item_in>-ref_1_s = ls_ecc_business-ref_1_s .
<fs_item_in>-purch_no_c = ls_ecc_business-purch_no_c .
<fs_item_inx>-ref_1 = 'X'.
<fs_item_inx>-purch_no_s = 'X'.
<fs_item_inx>-ref_1_s = 'X'.
<fs_item_inx>-purch_no_c = 'X'.
ENDIF.
" 3. 處理排程線映射 (Schedules) - 僅處理新增的項目
LOOP AT gt_ecc_schedules_out INTO DATA(ls_ecc_schedule).
" 檢查其對應的項目是否是我們這次要新增的
READ TABLE lt_items_in TRANSPORTING NO FIELDS
WITH KEY itm_number = ls_ecc_schedule-itm_number.
IF sy-subrc = 0.
APPEND INITIAL LINE TO lt_schedules_in ASSIGNING FIELD-SYMBOL(<fs_sched_in>).
APPEND INITIAL LINE TO lt_schedules_inx ASSIGNING FIELD-SYMBOL(<fs_sched_inx>).
<fs_sched_in>-itm_number = ls_ecc_schedule-itm_number.
<fs_sched_in>-sched_line = ls_ecc_schedule-sched_line.
<fs_sched_in>-req_qty = ls_ecc_schedule-req_qty.
<fs_sched_in>-req_date = ls_ecc_schedule-req_date.
<fs_sched_inx>-updateflag = 'I'.
<fs_sched_inx>-itm_number = ls_ecc_schedule-itm_number.
<fs_sched_inx>-sched_line = ls_ecc_schedule-sched_line.
<fs_sched_inx>-req_qty = 'X'.
<fs_sched_inx>-req_date = 'X'.
ENDIF.
ENDLOOP.
" 4. 處理價格條件映射 (Conditions) - 僅處理新增的項目
LOOP AT gt_ecc_conditions_out INTO DATA(ls_ecc_cond).
IF ls_ecc_cond-condorigin = 'A'OR
ls_ecc_cond-condorigin = '' .
CONTINUE.
ENDIF.
" 檢查其對應的項目是否是我們這次要新增的
READ TABLE lt_items_in TRANSPORTING NO FIELDS
WITH KEY itm_number = ls_ecc_cond-itm_number.
IF sy-subrc = 0.
APPEND INITIAL LINE TO lt_conditions_in ASSIGNING FIELD-SYMBOL(<fs_cond_in>).
APPEND INITIAL LINE TO lt_conditions_inx ASSIGNING FIELD-SYMBOL(<fs_cond_inx>).
<fs_cond_in>-itm_number = ls_ecc_cond-itm_number.
<fs_cond_in>-cond_st_no = ls_ecc_cond-cond_st_no.
<fs_cond_in>-cond_count = ls_ecc_cond-cond_count.
<fs_cond_in>-cond_type = ls_ecc_cond-cond_type.
<fs_cond_in>-cond_value = ls_ecc_cond-cond_value.
<fs_cond_in>-currency = ls_ecc_cond-currency.
<fs_cond_in>-cond_unit = ls_ecc_cond-cond_d_unt.
<fs_cond_in>-cond_p_unt = ls_ecc_cond-cond_p_unt.
<fs_cond_inx>-updateflag = 'I'.
<fs_cond_inx>-itm_number = ls_ecc_cond-itm_number.
<fs_cond_inx>-cond_st_no = ls_ecc_cond-cond_st_no.
<fs_cond_inx>-cond_count = ls_ecc_cond-cond_count.
<fs_cond_inx>-cond_type = ls_ecc_cond-cond_type.
<fs_cond_inx>-cond_value = 'X'.
<fs_cond_inx>-currency = 'X'.
<fs_cond_inx>-cond_unit = 'X'.
<fs_cond_inx>-cond_p_unt = 'X'.
ENDIF.
ENDLOOP.
" 5. 如果沒有新項目需要新增,直接提示並結束
IF lv_has_new_items = abap_false.
DATA(lt_dummy_msg) = VALUE bapiret2_t( ( type = 'I' message = 'No new items to add. Order is up-to-date.' ) ).
PERFORM f_append_alv_log USING p_vbeln p_vbeln '@08@' lt_dummy_msg. " @08@ 是綠燈
RETURN.
ENDIF.
" 6. 呼叫 BAPI 進行更新 (使用 BAPI_SALESORDER_CHANGE)
lv_s4_vbeln = p_vbeln.
CALL FUNCTION 'BAPI_SALESORDER_CHANGE'
EXPORTING
salesdocument = lv_s4_vbeln
order_header_in = ls_header_in " 新增: 雖然是空的,但顯式傳遞
order_header_inx = ls_header_inx
logic_switch = ls_logic_switch
TABLES
return = lt_return_s4
order_item_in = lt_items_in
order_item_inx = lt_items_inx
schedule_lines = lt_schedules_in
schedule_linesx = lt_schedules_inx
conditions_in = lt_conditions_in
conditions_inx = lt_conditions_inx.
" 7. 檢查結果並 Commit/Rollback
READ TABLE lt_return_s4 TRANSPORTING NO FIELDS WITH KEY type = 'E'.
IF sy-subrc <> 0.
READ TABLE lt_return_s4 TRANSPORTING NO FIELDS WITH KEY type = 'A'.
ENDIF.
IF sy-subrc <> 0. " 沒有錯誤 (E 或 A)
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'.
lv_status_icon = '@08@'. " 綠燈
" 確保有一筆成功訊息
READ TABLE lt_return_s4 TRANSPORTING NO FIELDS WITH KEY type = 'S'.
IF sy-subrc <> 0.
APPEND VALUE bapiret2( type = 'S' message = |Sales Order { lv_s4_vbeln } successfully updated with missing items.| ) TO lt_return_s4.
ENDIF.
ELSE.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
lv_status_icon = '@0A@'. " 紅燈
ENDIF.
" 記錄日誌
PERFORM f_append_alv_log USING p_vbeln lv_s4_vbeln lv_status_icon lt_return_s4.
ENDFORM.
&---------------------------------------------------------------------
*& Form FRM_GET_PLANT
&---------------------------------------------------------------------
*& text
&---------------------------------------------------------------------
*& --> LS_ECC_ITEM_PLANT
*& <-- LV_PLANT
&---------------------------------------------------------------------
FORM frm_get_plant USING uv_plant TYPE werks_d
CHANGING cv_plant TYPE werks_d.
CASE uv_plant.
WHEN '1100'.
cv_plant = '1000'.
WHEN '2100'.
cv_plant = '2000'.
WHEN OTHERS.
cv_plant = uv_plant.
ENDCASE.
ENDFORM.
浙公网安备 33010602011771号