REPORT zlegacy_order_fulfillment_audit MESSAGE-ID zsd_legacy. *---------------------------------------------------------------------* * Legacy enterprise report for Clean-Core / modernization testing * Fictional but realistic: SD order remediation, credit check, * inventory reconciliation, batch-input updates, RFC enrichment, * update-task logging, direct table access, classic ALV and downloads. *---------------------------------------------------------------------* TABLES: vbak, vbap, vbep, kna1, knb1, mara, marc, mard, tvko, t001, usr02, zsd_ord_risk, zsd_legacy_log. TYPE-POOLS: slis, abap. CONSTANTS: c_program TYPE syrepid VALUE 'ZLEGACY_ORDER_FULFILLMENT_AUDIT', c_destination TYPE rfcdest VALUE 'PRD_CREDIT_RFC', c_tcode_va02 TYPE tcode VALUE 'VA02', c_default_vkorg TYPE vkorg VALUE '1000', c_default_werks TYPE werks_d VALUE '1000', c_local_path TYPE string VALUE 'C:\\TEMP\\legacy_order_audit.csv', c_mail_sender TYPE adr6-smtp_addr VALUE 'batch-user@example.local', c_max_items TYPE i VALUE 999999, c_critical_score TYPE i VALUE 80, c_medium_score TYPE i VALUE 50. DATA: gv_batch_mode TYPE c VALUE 'X', gv_simulation TYPE c, gv_commit_counter TYPE i, gv_error_counter TYPE i, gv_warning_counter TYPE i, gv_success_counter TYPE i, gv_line_count TYPE i, gv_rule_hits TYPE i, gv_file_name TYPE string, gv_layout TYPE slis_layout_alv, gv_repid TYPE syrepid, gv_today TYPE sy-datum, gv_runtime_start TYPE sy-uzeit, gv_runtime_end TYPE sy-uzeit. TYPES: BEGIN OF ty_order, vbeln TYPE vbak-vbeln, auart TYPE vbak-auart, vkorg TYPE vbak-vkorg, vtweg TYPE vbak-vtweg, spart TYPE vbak-spart, kunnr TYPE vbak-kunnr, erdat TYPE vbak-erdat, ernam TYPE vbak-ernam, netwr TYPE vbak-netwr, waerk TYPE vbak-waerk, lifsk TYPE vbak-lifsk, faksk TYPE vbak-faksk, cmgst TYPE vbak-cmgst, bstnk TYPE vbak-bstnk, item_count TYPE i, open_qty TYPE vbap-kwmeng, confirmed_qty TYPE vbep-bmeng, stock_qty TYPE mard-labst, risk_score TYPE i, risk_text TYPE char80, proposed_block TYPE vbak-lifsk, action TYPE char30, message TYPE char255, changed_by_bdc TYPE abap_bool, credit_remote TYPE char20, END OF ty_order. TYPES: BEGIN OF ty_item, vbeln TYPE vbap-vbeln, posnr TYPE vbap-posnr, matnr TYPE vbap-matnr, werks TYPE vbap-werks, pstyv TYPE vbap-pstyv, kwmeng TYPE vbap-kwmeng, netwr TYPE vbap-netwr, lgort TYPE vbap-lgort, abgru TYPE vbap-abgru, mvgr1 TYPE mvgr1, mtart TYPE mara-mtart, matkl TYPE mara-matkl, labst TYPE mard-labst, reserved TYPE mard-insme, finding TYPE char100, END OF ty_item. TYPES: BEGIN OF ty_customer, kunnr TYPE kna1-kunnr, name1 TYPE kna1-name1, land1 TYPE kna1-land1, regio TYPE kna1-regio, ktokd TYPE kna1-ktokd, sperr TYPE kna1-sperr, bukrs TYPE knb1-bukrs, akont TYPE knb1-akont, zterm TYPE knb1-zterm, risk_class TYPE char10, credit_limit TYPE p LENGTH 15 DECIMALS 2, END OF ty_customer. TYPES: BEGIN OF ty_log, run_id TYPE char32, datum TYPE sy-datum, uzeit TYPE sy-uzeit, uname TYPE sy-uname, level TYPE char10, object TYPE char30, key_value TYPE char50, message TYPE char255, END OF ty_log. DATA: gt_orders TYPE STANDARD TABLE OF ty_order WITH DEFAULT KEY, gt_items TYPE STANDARD TABLE OF ty_item WITH DEFAULT KEY, gt_customers TYPE STANDARD TABLE OF ty_customer WITH DEFAULT KEY, gt_log TYPE STANDARD TABLE OF ty_log WITH DEFAULT KEY, gt_fieldcat TYPE slis_t_fieldcat_alv, gt_bdcdata TYPE STANDARD TABLE OF bdcdata WITH DEFAULT KEY, gt_bdcmsg TYPE STANDARD TABLE OF bdcmsgcoll WITH DEFAULT KEY. DATA: gs_order TYPE ty_order, gs_item TYPE ty_item, gs_customer TYPE ty_customer, gs_log TYPE ty_log, gs_bdcdata TYPE bdcdata. FIELD-SYMBOLS: TYPE ty_order, TYPE ty_item, TYPE ty_log. SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE text-001. SELECT-OPTIONS: s_vkorg FOR vbak-vkorg DEFAULT c_default_vkorg, s_vbeln FOR vbak-vbeln, s_kunnr FOR vbak-kunnr, s_erdat FOR vbak-erdat, s_matnr FOR vbap-matnr, s_werks FOR vbap-werks DEFAULT c_default_werks. PARAMETERS: p_days TYPE i DEFAULT 90, p_lim TYPE i DEFAULT 5000, p_block TYPE vbak-lifsk DEFAULT 'Z1', p_file TYPE string DEFAULT c_local_path LOWER CASE. SELECTION-SCREEN END OF BLOCK b01. SELECTION-SCREEN BEGIN OF BLOCK b02 WITH FRAME TITLE text-002. PARAMETERS: p_upd AS CHECKBOX DEFAULT ' ', p_bdc AS CHECKBOX DEFAULT 'X', p_rfc AS CHECKBOX DEFAULT 'X', p_mail AS CHECKBOX DEFAULT ' ', p_down AS CHECKBOX DEFAULT 'X', p_alv AS CHECKBOX DEFAULT 'X', p_trace AS CHECKBOX DEFAULT 'X'. SELECTION-SCREEN END OF BLOCK b02. INITIALIZATION. gv_today = sy-datum. gv_runtime_start = sy-uzeit. gv_repid = sy-repid. text-001 = 'Legacy order selection'. text-002 = 'Processing switches'. AT SELECTION-SCREEN. PERFORM validate_selection. START-OF-SELECTION. PERFORM authority_check. PERFORM initialize_run. PERFORM select_orders. PERFORM select_items. PERFORM enrich_customers. PERFORM enrich_materials_and_stock. PERFORM calculate_risk_scores. PERFORM apply_order_rules. PERFORM remote_credit_check. PERFORM decide_actions. PERFORM process_actions. PERFORM persist_run_log. PERFORM download_result_file. PERFORM send_summary_mail. END-OF-SELECTION. PERFORM build_fieldcatalog. PERFORM display_alv. PERFORM finalize_run. PERFORM write_run_statistics. FORM validate_selection. IF s_vkorg[] IS INITIAL. MESSAGE e001 WITH 'Sales organization is required'. ENDIF. IF p_lim IS INITIAL OR p_lim GT c_max_items. p_lim = 5000. ENDIF. IF p_days LT 0. MESSAGE e001 WITH 'Days cannot be negative'. ENDIF. IF p_file IS INITIAL. p_file = c_local_path. ENDIF. ENDFORM. FORM authority_check. AUTHORITY-CHECK OBJECT 'V_VBAK_VKO' ID 'VKORG' FIELD c_default_vkorg ID 'ACTVT' FIELD '03'. IF sy-subrc <> 0. PERFORM add_log USING 'ERROR' 'AUTH' sy-uname 'Missing display authorization for sales orders'. MESSAGE e001 WITH 'Missing sales order authorization'. ENDIF. IF p_upd = abap_true. AUTHORITY-CHECK OBJECT 'V_VBAK_VKO' ID 'VKORG' FIELD c_default_vkorg ID 'ACTVT' FIELD '02'. IF sy-subrc <> 0. MESSAGE e001 WITH 'Update mode requires change authorization'. ENDIF. ENDIF. ENDFORM. FORM initialize_run. DATA lv_guid TYPE char32. CALL FUNCTION 'GUID_CREATE' IMPORTING ev_guid_32 = lv_guid. gv_file_name = p_file. CLEAR: gt_orders, gt_items, gt_customers, gt_log. PERFORM add_log USING 'INFO' 'RUN' lv_guid 'Started legacy order fulfillment audit'. IF p_trace = abap_true. PERFORM count_orders_native. ENDIF. ENDFORM. FORM select_orders. DATA lv_from_date TYPE sy-datum. lv_from_date = sy-datum - p_days. SELECT * FROM vbak INTO CORRESPONDING FIELDS OF TABLE gt_orders UP TO p_lim ROWS WHERE vbeln IN s_vbeln AND vkorg IN s_vkorg AND kunnr IN s_kunnr AND erdat GE lv_from_date AND erdat IN s_erdat AND auart IN ('OR','ZOR','TA','RE','ZEXP') AND vbtyp = 'C'. IF sy-subrc <> 0. PERFORM add_log USING 'WARN' 'VBAK' 'NONE' 'No orders found for selection'. ENDIF. SORT gt_orders BY vbeln. DELETE ADJACENT DUPLICATES FROM gt_orders COMPARING vbeln. DESCRIBE TABLE gt_orders LINES gv_line_count. PERFORM add_log USING 'INFO' 'VBAK' gv_line_count 'Orders selected from VBAK'. ENDFORM. FORM select_items. IF gt_orders IS INITIAL. EXIT. ENDIF. SELECT * FROM vbap INTO CORRESPONDING FIELDS OF TABLE gt_items FOR ALL ENTRIES IN gt_orders WHERE vbeln = gt_orders-vbeln AND matnr IN s_matnr AND werks IN s_werks. LOOP AT gt_items ASSIGNING . SELECT SINGLE mtart matkl FROM mara INTO (-mtart, -matkl) WHERE matnr = -matnr. IF sy-subrc <> 0. -finding = 'Material master missing'. ENDIF. ENDLOOP. PERFORM add_log USING 'INFO' 'VBAP' sy-dbcnt 'Items selected and enriched'. ENDFORM. FORM enrich_customers. DATA lt_kunnr TYPE STANDARD TABLE OF kna1-kunnr. DATA lv_kunnr TYPE kna1-kunnr. LOOP AT gt_orders INTO gs_order. lv_kunnr = gs_order-kunnr. APPEND lv_kunnr TO lt_kunnr. ENDLOOP. SORT lt_kunnr. DELETE ADJACENT DUPLICATES FROM lt_kunnr. LOOP AT lt_kunnr INTO lv_kunnr. CLEAR gs_customer. SELECT SINGLE kunnr name1 land1 regio ktokd sperr FROM kna1 INTO CORRESPONDING FIELDS OF gs_customer WHERE kunnr = lv_kunnr. SELECT SINGLE bukrs akont zterm FROM knb1 INTO CORRESPONDING FIELDS OF gs_customer WHERE kunnr = lv_kunnr AND bukrs = '1000'. PERFORM derive_customer_risk CHANGING gs_customer. APPEND gs_customer TO gt_customers. ENDLOOP. ENDFORM. FORM derive_customer_risk CHANGING cs_customer TYPE ty_customer. IF cs_customer-sperr = 'X'. cs_customer-risk_class = 'BLOCKED'. cs_customer-credit_limit = 0. ELSEIF cs_customer-land1 <> 'DE' AND cs_customer-land1 <> 'AT' AND cs_customer-land1 <> 'CH'. cs_customer-risk_class = 'EXPORT'. cs_customer-credit_limit = '25000.00'. ELSEIF cs_customer-ktokd = 'ZINT'. cs_customer-risk_class = 'INTERNAL'. cs_customer-credit_limit = '999999.99'. ELSE. cs_customer-risk_class = 'NORMAL'. cs_customer-credit_limit = '100000.00'. ENDIF. ENDFORM. FORM enrich_materials_and_stock. LOOP AT gt_items ASSIGNING . SELECT SINGLE labst insme FROM mard INTO (-labst, -reserved) WHERE matnr = -matnr AND werks = -werks AND lgort = -lgort. IF sy-subrc <> 0. -finding = 'No plant/storage stock found'. ELSEIF -labst < -kwmeng. -finding = 'Available stock below ordered quantity'. ENDIF. ENDLOOP. ENDFORM. FORM calculate_risk_scores. DATA lv_item_count TYPE i. DATA lv_open_qty TYPE vbap-kwmeng. DATA lv_stock_qty TYPE mard-labst. DATA lv_customer_risk TYPE char10. DATA lv_days_old TYPE i. LOOP AT gt_orders ASSIGNING . CLEAR: lv_item_count, lv_open_qty, lv_stock_qty, lv_customer_risk. LOOP AT gt_items INTO gs_item WHERE vbeln = -vbeln. ADD 1 TO lv_item_count. lv_open_qty = lv_open_qty + gs_item-kwmeng. lv_stock_qty = lv_stock_qty + gs_item-labst. IF gs_item-finding IS NOT INITIAL. -risk_score = -risk_score + 10. ENDIF. ENDLOOP. READ TABLE gt_customers INTO gs_customer WITH KEY kunnr = -kunnr. IF sy-subrc = 0. lv_customer_risk = gs_customer-risk_class. IF gs_customer-risk_class = 'BLOCKED'. -risk_score = -risk_score + 40. ELSEIF gs_customer-risk_class = 'EXPORT'. -risk_score = -risk_score + 15. ENDIF. IF -netwr > gs_customer-credit_limit. -risk_score = -risk_score + 35. ENDIF. ELSE. -risk_score = -risk_score + 20. ENDIF. lv_days_old = sy-datum - -erdat. IF lv_days_old GT 180. -risk_score = -risk_score + 20. ELSEIF lv_days_old GT 90. -risk_score = -risk_score + 10. ENDIF. IF -lifsk IS NOT INITIAL OR -faksk IS NOT INITIAL. -risk_score = -risk_score + 10. ENDIF. IF lv_stock_qty < lv_open_qty. -risk_score = -risk_score + 15. ENDIF. -item_count = lv_item_count. -open_qty = lv_open_qty. -stock_qty = lv_stock_qty. IF -risk_score GE c_critical_score. -risk_text = 'Critical: customer, stock or credit exception'. ELSEIF -risk_score GE c_medium_score. -risk_text = 'Medium: manual review recommended'. ELSE. -risk_text = 'Low: no immediate action'. ENDIF. ENDLOOP. ENDFORM. FORM remote_credit_check. CHECK p_rfc = abap_true. LOOP AT gt_orders ASSIGNING . CALL FUNCTION 'Z_CREDIT_EXPOSURE_READ' DESTINATION c_destination EXPORTING iv_kunnr = -kunnr iv_vkorg = -vkorg IMPORTING ev_status = -credit_remote EXCEPTIONS system_failure = 1 MESSAGE -message communication_failure = 2 MESSAGE -message OTHERS = 3. IF sy-subrc <> 0. -credit_remote = 'RFC_FAILED'. -risk_score = -risk_score + 10. PERFORM add_log USING 'WARN' 'RFC' -vbeln -message. ELSEIF -credit_remote = 'RED'. -risk_score = -risk_score + 25. ENDIF. ENDLOOP. ENDFORM. FORM decide_actions. LOOP AT gt_orders ASSIGNING . IF -risk_score GE c_critical_score. -action = 'SET_DELIVERY_BLOCK'. -proposed_block = p_block. ELSEIF -risk_score GE c_medium_score. -action = 'CREATE_REVIEW_TASK'. ELSE. -action = 'NO_ACTION'. ENDIF. ENDLOOP. ENDFORM. FORM process_actions. LOOP AT gt_orders ASSIGNING . CASE -action. WHEN 'SET_DELIVERY_BLOCK'. IF p_upd = abap_true AND p_bdc = abap_true. PERFORM change_sales_order_bdc USING -vbeln -proposed_block CHANGING -message. ELSE. -message = 'Simulation: delivery block would be set'. ENDIF. IF p_trace = abap_true AND sy-batch = abap_false. PERFORM show_document_flow USING -vbeln. ENDIF. WHEN 'CREATE_REVIEW_TASK'. PERFORM create_legacy_review_task USING . WHEN OTHERS. -message = 'No action required'. ENDCASE. PERFORM update_legacy_log_task USING . ENDLOOP. ENDFORM. FORM change_sales_order_bdc USING iv_vbeln TYPE vbak-vbeln iv_block TYPE vbak-lifsk CHANGING cv_message TYPE char255. REFRESH: gt_bdcdata, gt_bdcmsg. PERFORM bdc_dynpro USING 'SAPMV45A' '0102'. PERFORM bdc_field USING 'BDC_CURSOR' 'VBAK-VBELN'. PERFORM bdc_field USING 'BDC_OKCODE' '/00'. PERFORM bdc_field USING 'VBAK-VBELN' iv_vbeln. PERFORM bdc_dynpro USING 'SAPMV45A' '4001'. PERFORM bdc_field USING 'BDC_OKCODE' '=KKAU'. PERFORM bdc_field USING 'VBAK-LIFSK' iv_block. PERFORM bdc_dynpro USING 'SAPMV45A' '4001'. PERFORM bdc_field USING 'BDC_OKCODE' '=SICH'. CALL TRANSACTION c_tcode_va02 USING gt_bdcdata MODE 'N' UPDATE 'S' MESSAGES INTO gt_bdcmsg. IF sy-subrc = 0. cv_message = 'Delivery block changed by BDC'. ADD 1 TO gv_success_counter. ELSE. cv_message = 'BDC failed for sales order'. ADD 1 TO gv_error_counter. PERFORM add_log USING 'ERROR' 'BDC' iv_vbeln cv_message. ENDIF. ENDFORM. FORM bdc_dynpro USING iv_program TYPE bdc_prog iv_dynpro TYPE bdc_dynr. CLEAR gs_bdcdata. gs_bdcdata-program = iv_program. gs_bdcdata-dynpro = iv_dynpro. gs_bdcdata-dynbegin = 'X'. APPEND gs_bdcdata TO gt_bdcdata. ENDFORM. FORM bdc_field USING iv_fnam TYPE fnam_____4 iv_fval TYPE any. CLEAR gs_bdcdata. gs_bdcdata-fnam = iv_fnam. gs_bdcdata-fval = iv_fval. APPEND gs_bdcdata TO gt_bdcdata. ENDFORM. FORM create_legacy_review_task USING is_order TYPE ty_order. DATA lv_text TYPE char255. CONCATENATE 'Review order' is_order-vbeln 'risk' is_order-risk_score INTO lv_text SEPARATED BY space. INSERT zsd_ord_risk FROM @( VALUE #( mandt = sy-mandt vbeln = is_order-vbeln risk_score = is_order-risk_score risk_text = is_order-risk_text erdat = sy-datum ernam = sy-uname ) ). IF sy-subrc <> 0. PERFORM add_log USING 'WARN' 'ZSD_ORD_RISK' is_order-vbeln 'Could not insert risk review task'. ELSE. PERFORM add_log USING 'INFO' 'ZSD_ORD_RISK' is_order-vbeln lv_text. ENDIF. ENDFORM. FORM update_legacy_log_task USING is_order TYPE ty_order. CALL FUNCTION 'Z_SD_LEGACY_LOG_WRITE' IN UPDATE TASK EXPORTING iv_vbeln = is_order-vbeln iv_score = is_order-risk_score iv_text = is_order-risk_text iv_user = sy-uname. ADD 1 TO gv_commit_counter. IF gv_commit_counter MOD 50 = 0. COMMIT WORK. ENDIF. ENDFORM. FORM persist_run_log. LOOP AT gt_log INTO gs_log. INSERT zsd_legacy_log FROM @( VALUE #( mandt = sy-mandt datum = gs_log-datum uzeit = gs_log-uzeit uname = gs_log-uname object = gs_log-object key_value = gs_log-key_value message = gs_log-message ) ). ENDLOOP. COMMIT WORK AND WAIT. ENDFORM. FORM download_result_file. CHECK p_down = abap_true. DATA lt_csv TYPE STANDARD TABLE OF string. DATA lv_line TYPE string. APPEND 'VBELN;KUNNR;VKORG;NETWR;WAERK;RISK_SCORE;ACTION;MESSAGE' TO lt_csv. LOOP AT gt_orders INTO gs_order. CONCATENATE gs_order-vbeln gs_order-kunnr gs_order-vkorg gs_order-netwr gs_order-waerk gs_order-risk_score gs_order-action gs_order-message INTO lv_line SEPARATED BY ';'. APPEND lv_line TO lt_csv. ENDLOOP. CALL FUNCTION 'GUI_DOWNLOAD' EXPORTING filename = gv_file_name filetype = 'ASC' TABLES data_tab = lt_csv EXCEPTIONS file_write_error = 1 no_batch = 2 gui_refuse_filetransfer = 3 invalid_type = 4 no_authority = 5 unknown_error = 6 OTHERS = 7. IF sy-subrc <> 0. PERFORM add_log USING 'WARN' 'GUI_DOWNLOAD' gv_file_name 'Download failed or not available in background'. ENDIF. ENDFORM. FORM send_summary_mail. CHECK p_mail = abap_true. DATA lt_objtxt TYPE STANDARD TABLE OF solisti1. DATA ls_objtxt TYPE solisti1. DATA lt_reclist TYPE STANDARD TABLE OF somlreci1. DATA ls_reclist TYPE somlreci1. DATA ls_docdata TYPE sodocchgi1. ls_docdata-obj_name = 'LEGACY_AUDIT'. ls_docdata-obj_descr = 'Legacy order audit result'. CONCATENATE 'Processed orders:' gv_line_count INTO ls_objtxt-line SEPARATED BY space. APPEND ls_objtxt TO lt_objtxt. CONCATENATE 'Errors:' gv_error_counter 'Warnings:' gv_warning_counter INTO ls_objtxt-line SEPARATED BY space. APPEND ls_objtxt TO lt_objtxt. ls_reclist-receiver = c_mail_sender. ls_reclist-rec_type = 'U'. APPEND ls_reclist TO lt_reclist. CALL FUNCTION 'SO_NEW_DOCUMENT_SEND_API1' EXPORTING document_data = ls_docdata document_type = 'RAW' put_in_outbox = 'X' TABLES object_content = lt_objtxt receivers = lt_reclist EXCEPTIONS too_many_receivers = 1 document_not_sent = 2 document_type_not_exist = 3 operation_no_authorization = 4 parameter_error = 5 x_error = 6 enqueue_error = 7 OTHERS = 8. ENDFORM. FORM build_fieldcatalog. PERFORM append_fieldcat USING 'VBELN' 'Sales Order' 12. PERFORM append_fieldcat USING 'KUNNR' 'Customer' 12. PERFORM append_fieldcat USING 'VKORG' 'Sales Org' 8. PERFORM append_fieldcat USING 'NETWR' 'Net Value' 15. PERFORM append_fieldcat USING 'RISK_SCORE' 'Risk' 8. PERFORM append_fieldcat USING 'RISK_TEXT' 'Risk Text' 40. PERFORM append_fieldcat USING 'ACTION' 'Action' 25. PERFORM append_fieldcat USING 'MESSAGE' 'Message' 60. ENDFORM. FORM append_fieldcat USING iv_field TYPE slis_fieldname iv_text TYPE char40 iv_len TYPE i. DATA ls_fieldcat TYPE slis_fieldcat_alv. CLEAR ls_fieldcat. ls_fieldcat-fieldname = iv_field. ls_fieldcat-seltext_m = iv_text. ls_fieldcat-outputlen = iv_len. APPEND ls_fieldcat TO gt_fieldcat. ENDFORM. FORM display_alv. IF p_alv = abap_false. PERFORM show_review_screen. RETURN. ENDIF. gv_layout-zebra = 'X'. gv_layout-colwidth_optimize = 'X'. CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING i_callback_program = gv_repid is_layout = gv_layout it_fieldcat = gt_fieldcat TABLES t_outtab = gt_orders EXCEPTIONS program_error = 1 OTHERS = 2. ENDFORM. FORM add_log USING iv_level TYPE char10 iv_object TYPE char30 iv_key TYPE any iv_message TYPE char255. CLEAR gs_log. gs_log-datum = sy-datum. gs_log-uzeit = sy-uzeit. gs_log-uname = sy-uname. gs_log-level = iv_level. gs_log-object = iv_object. gs_log-key_value = iv_key. gs_log-message = iv_message. APPEND gs_log TO gt_log. IF iv_level = 'WARN'. ADD 1 TO gv_warning_counter. ELSEIF iv_level = 'ERROR'. ADD 1 TO gv_error_counter. ENDIF. ENDFORM. FORM finalize_run. gv_runtime_end = sy-uzeit. IF gv_commit_counter GT 0. COMMIT WORK AND WAIT. ENDIF. PERFORM add_log USING 'INFO' 'RUN' sy-repid 'Finished legacy order fulfillment audit'. ENDFORM. FORM count_orders_native. DATA lv_count TYPE i. EXEC SQL. SELECT COUNT(*) INTO :lv_count FROM VBAK WHERE MANDT = :sy-mandt ENDEXEC. IF lv_count GT 0. PERFORM add_log USING 'INFO' 'NATIVE_SQL' lv_count 'Orders in the client before selection'. ENDIF. ENDFORM. FORM show_document_flow USING iv_vbeln TYPE vbak-vbeln. SUBMIT rv_order_flow_information WITH p_vbeln = iv_vbeln AND RETURN. ENDFORM. FORM show_review_screen. CALL SCREEN 9000 STARTING AT 10 5 ENDING AT 120 25. ENDFORM. MODULE status_9000 OUTPUT. SET PF-STATUS 'MAIN'. SET TITLEBAR 'LEGACY'. ENDMODULE. MODULE user_command_9000 INPUT. CASE sy-ucomm. WHEN 'BACK' OR 'CANC' OR 'EXIT'. LEAVE TO SCREEN 0. WHEN OTHERS. MESSAGE s001 WITH 'Legacy dynpro command ignored'. ENDCASE. ENDMODULE. *---------------------------------------------------------------------* * Order rules: sixteen checks the sales department added one by one * (tickets SD-1042 to SD-1190). Each one adds to the risk score of the * order and names its reason in the run log. *---------------------------------------------------------------------* FORM apply_order_rules. DATA lv_score TYPE i. DATA lv_note TYPE char100. DATA lv_text TYPE char255. CHECK gt_orders IS NOT INITIAL. LOOP AT gt_orders ASSIGNING . CLEAR: lv_score, lv_note. PERFORM rule_custom_order_type USING CHANGING lv_score lv_note. PERFORM rule_foreign_high_value USING CHANGING lv_score lv_note. PERFORM rule_purchase_reference USING CHANGING lv_score lv_note. PERFORM rule_creator_user USING CHANGING lv_score lv_note. PERFORM rule_credit_status USING CHANGING lv_score lv_note. PERFORM rule_billing_block USING CHANGING lv_score lv_note. PERFORM rule_rejected_items USING CHANGING lv_score lv_note. PERFORM rule_schedule_confirmed USING CHANGING lv_score lv_note. PERFORM rule_new_customer USING CHANGING lv_score lv_note. PERFORM rule_payment_terms USING CHANGING lv_score lv_note. PERFORM rule_cross_border USING CHANGING lv_score lv_note. PERFORM rule_material_group USING CHANGING lv_score lv_note. PERFORM rule_stock_shortage USING CHANGING lv_score lv_note. PERFORM rule_weekend_entry USING CHANGING lv_score lv_note. PERFORM rule_duplicate_reference USING CHANGING lv_score lv_note. PERFORM rule_overdue_delivery USING CHANGING lv_score lv_note. IF lv_score <> 0. ADD 1 TO gv_rule_hits. -risk_score = -risk_score + lv_score. lv_text = lv_note. PERFORM add_log USING 'INFO' 'RULES' -vbeln lv_text. ENDIF. ENDLOOP. ENDFORM. FORM rule_custom_order_type USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Custom order types bypass the standard pricing procedure (SD-1042) CASE is_order-auart. WHEN 'ZOR'. IF is_order-vkorg = c_default_vkorg. cv_score = cv_score + 1. CONCATENATE cv_note 'custom order type' INTO cv_note SEPARATED BY space. ENDIF. WHEN 'ZEXP'. cv_score = cv_score + 3. CONCATENATE cv_note 'export order type' INTO cv_note SEPARATED BY space. WHEN 'RE'. cv_score = cv_score + 2. CONCATENATE cv_note 'returns order' INTO cv_note SEPARATED BY space. ENDCASE. ENDFORM. FORM rule_foreign_high_value USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Large orders in a foreign currency carry an exchange-rate risk (SD-1051) DATA lv_amount TYPE p LENGTH 15 DECIMALS 2. CHECK is_order-waerk <> 'EUR'. lv_amount = is_order-netwr. IF lv_amount GT 500000. cv_score = cv_score + 5. CONCATENATE cv_note 'foreign currency above 500000' INTO cv_note SEPARATED BY space. ELSEIF lv_amount GT 100000. cv_score = cv_score + 2. CONCATENATE cv_note 'foreign currency above 100000' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_purchase_reference USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Orders without a customer reference cannot be matched to a contract (SD-1063) IF is_order-bstnk IS INITIAL. cv_score = cv_score + 2. CONCATENATE cv_note 'no customer reference' INTO cv_note SEPARATED BY space. ELSEIF is_order-bstnk CP 'TEST*' OR is_order-bstnk CP 'DUMMY*'. cv_score = cv_score + 3. CONCATENATE cv_note 'suspicious purchase reference' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_creator_user USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Orders entered by interfaces or by users who have left need a second look (SD-1078) DATA: lv_ustyp TYPE usr02-ustyp, lv_gltgb TYPE usr02-gltgb. SELECT SINGLE ustyp gltgb FROM usr02 INTO (lv_ustyp, lv_gltgb) WHERE bname = is_order-ernam. IF sy-subrc <> 0. cv_score = cv_score + 3. CONCATENATE cv_note 'creator no longer exists' INTO cv_note SEPARATED BY space. ELSEIF lv_ustyp = 'B' OR lv_ustyp = 'C'. cv_score = cv_score + 5. CONCATENATE cv_note 'created by a system user' INTO cv_note SEPARATED BY space. ELSEIF lv_gltgb IS NOT INITIAL AND lv_gltgb < sy-datum. cv_score = cv_score + 2. CONCATENATE cv_note 'creator account expired' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_credit_status USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Overall credit status of the order as the credit check left it (SD-1085) CASE is_order-cmgst. WHEN 'B'. cv_score = cv_score + 10. CONCATENATE cv_note 'credit check failed' INTO cv_note SEPARATED BY space. WHEN 'C'. cv_score = cv_score + 5. CONCATENATE cv_note 'credit partly released' INTO cv_note SEPARATED BY space. WHEN 'D'. cv_score = cv_score + 1. CONCATENATE cv_note 'credit released by hand' INTO cv_note SEPARATED BY space. ENDCASE. ENDFORM. FORM rule_billing_block USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "A billing block without a delivery block ships goods nobody invoices (SD-1092) IF is_order-faksk IS NOT INITIAL AND is_order-lifsk IS INITIAL. cv_score = cv_score + 4. CONCATENATE cv_note 'billing block without delivery block' INTO cv_note SEPARATED BY space. ENDIF. "An order an earlier run already blocked is not blocked twice IF is_order-lifsk = p_block. cv_score = cv_score - 5. CONCATENATE cv_note 'already blocked by an earlier run' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_rejected_items USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Orders whose items are all rejected stay open forever (SD-1104) DATA: lv_total TYPE i, lv_rejected TYPE i. LOOP AT gt_items INTO gs_item WHERE vbeln = is_order-vbeln. ADD 1 TO lv_total. IF gs_item-abgru IS NOT INITIAL. ADD 1 TO lv_rejected. ENDIF. ENDLOOP. IF lv_total GT 0 AND lv_rejected = lv_total. cv_score = cv_score + 8. CONCATENATE cv_note 'every item rejected' INTO cv_note SEPARATED BY space. ELSEIF lv_rejected GT 0. cv_score = cv_score + 2. CONCATENATE cv_note 'items partly rejected' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_schedule_confirmed USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Confirmed quantity of the schedule lines against the ordered quantity (SD-1117) DATA lv_confirmed TYPE vbep-bmeng. SELECT SUM( bmeng ) FROM vbep INTO lv_confirmed WHERE vbeln = is_order-vbeln. IF lv_confirmed = 0 AND is_order-open_qty GT 0. cv_score = cv_score + 6. CONCATENATE cv_note 'nothing confirmed' INTO cv_note SEPARATED BY space. ELSEIF lv_confirmed < is_order-open_qty. cv_score = cv_score + 3. CONCATENATE cv_note 'partly confirmed' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_new_customer USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "A large first order from a customer created this month (SD-1126) DATA: lv_erdat TYPE kna1-erdat, lv_days TYPE i. SELECT SINGLE erdat FROM kna1 INTO lv_erdat WHERE kunnr = is_order-kunnr. CHECK sy-subrc = 0. lv_days = sy-datum - lv_erdat. IF lv_days LT 30 AND is_order-netwr GT 50000. cv_score = cv_score + 6. CONCATENATE cv_note 'new customer with a large order' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_payment_terms USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Extended payment terms are granted by the sales manager only (SD-1133) READ TABLE gt_customers INTO gs_customer WITH KEY kunnr = is_order-kunnr. CHECK sy-subrc = 0. IF gs_customer-zterm CP 'Z9*'. cv_score = cv_score + 4. CONCATENATE cv_note 'extended payment terms' INTO cv_note SEPARATED BY space. ELSEIF gs_customer-zterm IS INITIAL. cv_score = cv_score + 2. CONCATENATE cv_note 'no payment terms in company code 1000' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_cross_border USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Deliveries outside the country of the company code need export papers (SD-1149) DATA: lv_bukrs TYPE tvko-bukrs, lv_land1 TYPE t001-land1. SELECT SINGLE bukrs FROM tvko INTO lv_bukrs WHERE vkorg = is_order-vkorg. CHECK sy-subrc = 0. SELECT SINGLE land1 FROM t001 INTO lv_land1 WHERE bukrs = lv_bukrs. READ TABLE gt_customers INTO gs_customer WITH KEY kunnr = is_order-kunnr. IF sy-subrc = 0 AND gs_customer-land1 <> lv_land1. cv_score = cv_score + 3. CONCATENATE cv_note 'customer outside the company code country' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_material_group USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Material groups Z9* are under quality review (SD-1158) LOOP AT gt_items INTO gs_item WHERE vbeln = is_order-vbeln. IF gs_item-matkl CP 'Z9*'. cv_score = cv_score + 2. CONCATENATE cv_note 'material group under review' INTO cv_note SEPARATED BY space. EXIT. ENDIF. ENDLOOP. ENDFORM. FORM rule_stock_shortage USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Stock situation of the whole order, not of a single item (SD-1166) DATA lv_half TYPE vbap-kwmeng. lv_half = is_order-open_qty / 2. IF is_order-stock_qty = 0 AND is_order-open_qty GT 0. cv_score = cv_score + 7. CONCATENATE cv_note 'no stock at all' INTO cv_note SEPARATED BY space. ELSEIF is_order-stock_qty < lv_half. cv_score = cv_score + 3. CONCATENATE cv_note 'less than half in stock' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_weekend_entry USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Orders entered on a weekend were not checked by the order desk (SD-1177) DATA lv_day TYPE scal-indicator. CALL FUNCTION 'DATE_COMPUTE_DAY' EXPORTING date = is_order-erdat IMPORTING day = lv_day. IF lv_day = '6' OR lv_day = '7'. cv_score = cv_score + 2. CONCATENATE cv_note 'entered on a weekend' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_duplicate_reference USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "The same customer reference on two open orders is usually a double entry (SD-1183) DATA lv_other TYPE vbak-vbeln. CHECK is_order-bstnk IS NOT INITIAL. SELECT SINGLE vbeln FROM vbak INTO lv_other WHERE kunnr = is_order-kunnr AND bstnk = is_order-bstnk AND vbeln <> is_order-vbeln. IF sy-subrc = 0. cv_score = cv_score + 6. CONCATENATE cv_note 'customer reference also on order' lv_other INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM rule_overdue_delivery USING is_order TYPE ty_order CHANGING cv_score TYPE i cv_note TYPE char100. "Schedule lines whose delivery date has passed with quantity still open (SD-1190) DATA: lv_edatu TYPE vbep-edatu, lv_days TYPE i. SELECT MIN( edatu ) FROM vbep INTO lv_edatu WHERE vbeln = is_order-vbeln AND bmeng > 0. CHECK lv_edatu IS NOT INITIAL AND lv_edatu < sy-datum. lv_days = sy-datum - lv_edatu. IF lv_days GT 30. cv_score = cv_score + 5. CONCATENATE cv_note 'delivery overdue by more than 30 days' INTO cv_note SEPARATED BY space. ELSE. cv_score = cv_score + 2. CONCATENATE cv_note 'delivery overdue' INTO cv_note SEPARATED BY space. ENDIF. ENDFORM. FORM write_run_statistics. "Printed spool list for the batch job log of the order desk gv_success_counter = gv_line_count - gv_error_counter. FORMAT COLOR COL_HEADING. WRITE: / 'Legacy order fulfillment audit', sy-datum, gv_runtime_start, gv_runtime_end. FORMAT RESET. ULINE. WRITE: / 'Orders selected: ', gv_line_count, / 'Orders processed: ', gv_success_counter, / 'Orders with rules:', gv_rule_hits, / 'Warnings: ', gv_warning_counter, / 'Errors: ', gv_error_counter, / 'Commits: ', gv_commit_counter. ULINE. ENDFORM. * ------------------------------------------------------------------- * End marker: exact 1000 LOC variant for Clean-Core platform tests. * The scenario intentionally contains enterprise legacy patterns. * Expected findings: direct table access, RFC, BDC, update task, GUI. * Expected findings: dynpro module, native SQL, classic ALV, hardcoding. * Use this object as synthetic input only, not for productive SAP import. * -------------------------------------------------------------------