从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.

posted on 2026-08-31 09:03  加西亚1986  阅读(31)  评论(0)    收藏  举报

导航