ABAP--利用SO_NEW_DOCUMENT_ATT_SEND_API1实现动态EXCEL附件邮件发送
1. 动态生成EXCEL数据的核心逻辑在ABAP开发中动态生成EXCEL数据是个高频需求。我处理过不少类似场景比如每月自动发送销售报表、每日库存预警等。核心思路其实很简单先把业务数据准备好然后转换成EXCEL能识别的格式最后通过邮件发出去。先说说数据准备这块。以订单数据为例通常会从VBAK、VBAP这些标准表里取数。我习惯用内表来暂存数据这样后续处理起来方便。比如DATA: lt_orders TYPE TABLE OF vbap, ls_order TYPE vbap. SELECT * FROM vbap INTO TABLE lt_orders WHERE vbeln IN so_vbeln. so_vbeln是选择屏幕参数转换数据格式时有个坑要注意EXCEL对日期、数字格式很敏感。我吃过亏直接输出SAP的日期格式EXCEL会认不出来。后来学乖了一定要先转换DATA: lv_date_char TYPE char10. WRITE ls_order-audat TO lv_date_char DD/MM/YYYY. 转成DD/MM/YYYY格式2. 构建EXCEL文件内容生成EXCEL内容这部分最考验细节处理。我常用的方法是先构建表头再逐行填充数据。这里分享个实用技巧用制表符(TAB)分隔列用回车换行(CR_LF)分隔行。CONSTANTS: gc_tab TYPE c VALUE cl_abap_char_utilitieshorizontal_tab, gc_crlf TYPE c VALUE cl_abap_char_utilitiescr_lf. DATA: lv_excel_string TYPE string. 构建表头 CONCATENATE 订单号 gc_tab 物料号 gc_tab 数量 gc_tab 日期 gc_crlf INTO lv_excel_string.处理数据行时要注意特殊字符转义。有次遇到物料描述里有符号直接输出导致EXCEL解析出错。后来我都用下面这个函数处理CALL FUNCTION CONVERSION_EXIT_ALPHA_OUTPUT EXPORTING input ls_order-matnr IMPORTING output lv_matnr_out.3. 转换二进制数据ABAP的字符串要转成二进制才能作为附件发送。这个转换过程看似简单但有性能陷阱。我最初的做法是直接循环切割字符串DATA: lt_bin_data TYPE solix_tab, lv_offset TYPE i, lv_length TYPE i. lv_length strlen( lv_excel_string ). DO lv_length TIMES. APPEND INITIAL LINE TO lt_bin_data ASSIGNING FIELD-SYMBOL(fs_bin). fs_bin-line lv_excel_stringlv_offset(255). lv_offset lv_offset 255. ENDDO.后来发现数据量大时这样效率太低。改进方案是先计算需要多少行再批量处理DATA: lv_rows TYPE i, lv_last_row_len TYPE i. lv_rows lv_length DIV 255. lv_last_row_len lv_length MOD 255. DO lv_rows TIMES. APPEND INITIAL LINE TO lt_bin_data ASSIGNING FIELD-SYMBOL(fs_bin). fs_bin-line lv_excel_stringlv_offset(255). lv_offset lv_offset 255. ENDDO. IF lv_last_row_len 0. APPEND INITIAL LINE TO lt_bin_data ASSIGNING fs_bin. fs_bin-line lv_excel_stringlv_offset(lv_last_row_len). ENDIF.4. 配置邮件发送参数SO_NEW_DOCUMENT_ATT_SEND_API1这个函数用起来要注意几个关键参数。packing_list的配置直接影响附件能否正常显示。我建议按这个模板来设置DATA: ls_packing TYPE sopcklsti1, lt_packing TYPE TABLE OF sopcklsti1. 邮件正文部分 ls_packing-head_start 1. ls_packing-head_num 0. ls_packing-body_start 1. ls_packing-body_num lines( lt_body_text ). ls_packing-doc_type RAW. APPEND ls_packing TO lt_packing. 附件部分 ls_packing-transf_bin X. ls_packing-head_start 1. ls_packing-head_num 1. ls_packing-body_start 1. ls_packing-body_num lines( lt_bin_data ). ls_packing-doc_type XLS. ls_packing-obj_descr 销售报表. 附件显示名称 ls_packing-obj_name SALES_REPORT. APPEND ls_packing TO lt_packing.收件人列表的配置也有讲究。如果需要同时发给多人建议这样处理DATA: lt_receivers TYPE STANDARD TABLE OF somlreci1, ls_receiver TYPE somlreci1. ls_receiver-rec_type U. Internet地址 ls_receiver-receiver user1example.com. ls_receiver-com_type INT. APPEND ls_receiver TO lt_receivers. ls_receiver-receiver user2example.com. APPEND ls_receiver TO lt_receivers.5. 调用发送函数及异常处理最后调用发送函数时这几个参数特别重要DATA: ls_doc_data TYPE sodocchgi1. ls_doc_data-obj_langu sy-langu. 使用当前登录语言 ls_doc_data-obj_name SALES_REPORT. ls_doc_data-obj_descr 月度销售报表. CALL FUNCTION SO_NEW_DOCUMENT_ATT_SEND_API1 EXPORTING document_data ls_doc_data put_in_outbox X 存入发件箱 commit_work X 自动提交 IMPORTING sent_to_all lv_sent_flag TABLES packing_list lt_packing contents_bin lt_bin_data contents_txt lt_body_text receivers lt_receivers 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.异常处理要特别注意。有次我遇到document_not_sent错误排查半天发现是SMTP配置有问题。建议这样处理异常IF sy-subrc 0. CASE sy-subrc. WHEN 1. MESSAGE 收件人数量超过限制 TYPE E. WHEN 2. MESSAGE 文档未发送检查SMTP配置 TYPE E. WHEN OTHERS. MESSAGE ID sy-msgid TYPE E NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDCASE. ENDIF.6. 实际项目中的优化技巧在真实项目中我还会做这些优化添加日志记录记录每次发送的时间、收件人、数据量等信息方便后续排查问题。DATA: lt_log TYPE TABLE OF zmail_log, ls_log TYPE zmail_log. ls_log-send_date sy-datum. ls_log-send_time sy-uzeit. ls_log-receivers lines( lt_receivers ). ls_log-data_size lines( lt_bin_data ). APPEND ls_log TO lt_log. MODIFY zmail_log FROM TABLE lt_log.支持多种文件格式通过参数控制生成XLS或XLSX格式。XLSX需要调用CL_ABAP_XLSX_DOCUMENTIF p_filetype XLSX. DATA(lo_excel) NEW cl_abap_xlsx_document( ). 添加工作表等操作 lo_excel-add_sheet( Sheet1 ). 获取二进制内容 lo_excel-get_document( IMPORTING ex_content lt_bin_data ). ENDIF.处理超大附件当数据量很大时建议分多个附件发送DATA: lv_part TYPE i VALUE 1. DO 10 TIMES. 假设分10个文件 提取部分数据生成附件 lv_attachment_name |销售报表_部分{ lv_part }.xls|. 设置packing_list时指定不同附件名 ls_packing-obj_name lv_attachment_name. 调用发送函数... lv_part lv_part 1. ENDDO.添加发送前的数据预览在选择屏幕增加预览按钮让用户可以确认数据后再发送MODULE user_command_0100 INPUT. CASE sy-ucomm. WHEN PREVIEW. 调用ALV显示将要发送的数据 PERFORM show_data_preview USING lt_orders. ENDCASE. ENDMODULE.7. 常见问题排查指南在实际使用中这几个问题我遇到最多附件打不开通常是二进制转换或packing_list配置问题。检查doc_type是否正确设置为XLStransf_bin是否设置为X二进制数据是否完整中文乱码确保邮件正文的字符集设置为UTF-8doc_data-obj_langu设置正确收件人邮箱支持中文显示发送超时大数据量时可能发生。解决方案增加WAIT UP TO 5 SECONDS等待时间分批次发送后台作业执行收件人收不到邮件检查SMTP服务器配置发件人邮箱是否有发送权限是否被收件方服务器拦截性能优化当处理大量数据时使用FOR ALL ENTRIES替代多次SELECT使用FIELD-SYMBOL减少数据拷贝考虑使用CL_SALV_TABLE快速生成Excel8. 扩展应用场景这个技术不仅限于发送订单数据还可以用在很多场景定期报表自动发送通过后台作业每月1号自动发送上月销售报表 在程序属性中设置为后台作业 在INITIALIZATION中设置默认日期 IF sy-batch IS NOT INITIAL. p_date sy-datum - 1. 昨天 PERFORM send_report USING p_date. LEAVE PROGRAM. ENDIF.异常预警通知当库存低于安全库存时自动发送预警邮件SELECT * FROM mard INTO TABLE DATA(lt_stock) WHERE labst sicher. 安全库存 IF lt_stock IS NOT INITIAL. PERFORM send_stock_alert USING lt_stock. ENDIF.审批结果通知当采购申请审批通过后自动发送带附件的通知 在BADI ME_PROCESS_PO_CUST中调用发送逻辑 METHOD if_ex_me_process_po_cust~process_po. IF is_po_header-approval APPROVED. 审批通过 PERFORM send_po_approval_email USING is_po_header. ENDIF. ENDMETHOD.数据备份将重要数据定期以Excel附件形式发送归档 在程序选择屏幕设置归档日期范围 PERFORM backup_data USING p_date_from p_date_to.系统监控定时发送系统性能监控报表 使用ST03N等事务码获取的性能数据 PERFORM collect_performance_data. PERFORM send_performance_report.这些场景的实现核心都是类似的准备数据→生成Excel→发送邮件。掌握了这个基本模式就能应对各种业务需求。我在项目中还遇到过需要发送带多个附件的场景比如同时发送Excel和PDF这时只需要在packing_list中配置多个条目即可。