Skip to content

销售交货开票报表

报表主要用于跟踪销售订单后续交货和开票情况,核心是这三者的关联查询。

主程序
*&---------------------------------------------------------------------*
*& Report ZSDREPORT
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*& 作者 Nefevcore
*& 描述 营销报表
*&---------------------------------------------------------------------*
REPORT zsdreport.

INCLUDE zsdreport_top.
INCLUDE zsdreport_sel.
INCLUDE zsdreport_frm.
INCLUDE zsdreport_alv.

START-OF-SELECTION.
  PERFORM frm_main.
全局参数
*&---------------------------------------------------------------------*
*& 包含               ZSDREPORT_TOP
*&---------------------------------------------------------------------*
" 数量和金额
TYPES ty_amount_p16_4 TYPE p LENGTH 16 DECIMALS 4.
TYPES ty_quan_p16_3 TYPE p LENGTH 16 DECIMALS 3.

" 销售
TYPES:
  BEGIN OF ty_va,
    vbeln  TYPE vbap-vbeln, " 销售订单号
    posnr  TYPE vbap-posnr, " 销售订单行号
    vbtyp  TYPE vbak-vbtyp, " 凭证类型
    auart  TYPE vbak-auart, " 销售订单类型
    vkorg  TYPE vbak-vkorg, " 销售组织
    vtweg  TYPE vbak-vtweg, " 分销渠道
    spart  TYPE vbak-spart, " 产品组
    kwmeng TYPE vbap-kwmeng, " 订单数量
    zmeng  TYPE vbap-zmeng, " 目标数量
    meins  TYPE vbap-meins, " 单位
    kzwi1  TYPE vbap-kzwi1, " 销售小计
    waerk  TYPE vbap-waerk, " 货币
    knumv  TYPE vbak-knumv, " 条件号
    kbetr  TYPE prcd_elements-kbetr, " 定价值
    bstkd  TYPE vbkd-bstkd, " 客户参考
  END OF ty_va.

" 交货
TYPES:
  BEGIN OF ty_vl,
    vbeln     TYPE lips-vbeln, " 交货单号
    posnr     TYPE lips-posnr, " 行项目号
    vbtyp     TYPE likp-vbtyp, " 销售和分销凭证类别
    lfart     TYPE likp-lfart, " 交货类型
    wadat_ist TYPE likp-wadat_ist, " 实际交货日期
    uecha     TYPE lips-uecha, " 上层项目批
    wbsta     TYPE lips-wbsta, " 交货状态
    lfimg     TYPE lips-lfimg, " 发货数量
    meins     TYPE lips-meins, " 单位
  END OF ty_vl.

" 发票
TYPES:
  BEGIN OF ty_vf,
    vbeln TYPE vbrp-vbeln, " 发票
    posnr TYPE vbrp-posnr, " 发票行
    vbtyp TYPE vbrk-vbtyp, " 发票凭证类别
    fkdat TYPE vbrk-fkdat, " 开票日期
    fkimg TYPE vbrp-fkimg, " 开票数量
    meins TYPE vbrp-meins, " 单位
    kzwi1 TYPE vbrp-kzwi1, " 发票小计
    waerk TYPE vbrp-waerk, " 货币
  END OF ty_vf.

" 通用
TYPES:
  BEGIN OF ty_general,
    kunnr      TYPE likp-kunnr, " 送达方编码
    kunnr_text TYPE string, " 送达方名称
    kunag      TYPE likp-kunag, " 签署方编码
    kunag_text TYPE string, " 签署方名称
    kunre      TYPE kna1-kunnr, " 开票方编码
    kunre_text TYPE string, " 开票方名称
    kunrg      TYPE kna1-kunnr, " 付款方编码
    kunrg_text TYPE string, " 付款方名称
    matnr      TYPE mara-matnr, " 物料号
    maktx      TYPE makt-maktx,  " 物料描述
    werks      TYPE t001w-werks, " 工厂
    lgort      TYPE t001l-lgort, " 库位
    charg      TYPE mch1-charg, " SAP批次号
    pspnr      TYPE prps-pspnr, " WBS
    posid      TYPE prps-posid, " WBS
  END OF ty_general.

" ALV报表
TYPES:
  BEGIN OF ty_data.
    INCLUDE TYPE ty_va AS va RENAMING WITH SUFFIX _va.
    INCLUDE TYPE ty_vl AS vl RENAMING WITH SUFFIX _vl.
    INCLUDE TYPE ty_vf AS vf RENAMING WITH SUFFIX _vf.
    INCLUDE TYPE ty_general.
TYPES:
    zfactor   TYPE i, " 正负因子
    ztax_rate TYPE ty_amount_p16_4, " 税率

    zamount1  TYPE ty_amount_p16_4, " 销售单价
    zquan2    TYPE ty_quan_p16_3, " 销售数量
    zamount3  TYPE ty_amount_p16_4, " 销售含税总金额

    zamount4  TYPE ty_amount_p16_4, " 发货单价(取销售单价)
    zquan5    TYPE ty_quan_p16_3, " 交货数量
    zamount6  TYPE ty_amount_p16_4, " 发货含税总金额
    zquan7    TYPE ty_quan_p16_3, " 未交货数量
    zamount8  TYPE ty_amount_p16_4, " 未发货含税总金额

    zamount9  TYPE ty_amount_p16_4, " 开票单价
    zquan10   TYPE ty_quan_p16_3, " 开票数量
    zamount11 TYPE ty_amount_p16_4, " 开票含税总金额
    zquan12   TYPE ty_quan_p16_3, " 未开票数量
    zamount13 TYPE ty_amount_p16_4, " 未开票含税总金额

    zsel      TYPE xfeld,
    mtype     TYPE bapi_mtype,
    msg       TYPE bapi_msg,
    t_scol    TYPE lvc_t_scol,
    t_styl    TYPE lvc_t_styl,
  END OF ty_data.
TYPES tt_data TYPE STANDARD TABLE OF ty_data.
DATA gt_data TYPE tt_data.

" 查询优化
" 按正常流程,需要先查询销售或交货,最后到发票
" 如果用户仅在筛选界面输入发票,等同全量查询
" 这样耗时太长,因此可以根据选择界面输入,预先查询
DATA:
  BEGIN OF gs_preload,
    va TYPE xfeld,
    vl TYPE xfeld,
    vf TYPE xfeld,
  END OF gs_preload.

" 键值
TYPES:
  BEGIN OF ty_sdkey,
    vbeln TYPE vbeln,
    posnr TYPE posnr,
  END OF ty_sdkey.
TYPES tt_sdkey TYPE STANDARD TABLE OF ty_sdkey.
DATA gt_sdkey TYPE tt_sdkey.

" 优化,程序写完后,多次出现过滤冲销发票的代码
TYPES:
  BEGIN OF ty_sfakn.
    INCLUDE TYPE ty_sdkey AS va RENAMING WITH SUFFIX _va.
    INCLUDE TYPE ty_sdkey AS vl RENAMING WITH SUFFIX _vl.
    INCLUDE TYPE ty_sdkey AS vf RENAMING WITH SUFFIX _vf.
TYPES:
    sfakn TYPE vbrk-sfakn,
  END OF ty_sfakn.
TYPES tt_sfakn TYPE STANDARD TABLE OF ty_sfakn.
选择屏幕
*&---------------------------------------------------------------------*
*& 包含               ZSDREPORT_SEL
*&---------------------------------------------------------------------*
TABLES vbak.
TABLES vbap.
TABLES vbrk.
TABLES likp.
TABLES prps.

PARAMETERS cb1 AS CHECKBOX DEFAULT abap_true.
PARAMETERS cb2 AS CHECKBOX DEFAULT abap_true.

SELECTION-SCREEN ULINE.

" 选择条件
SELECTION-SCREEN BEGIN OF BLOCK b900 WITH FRAME TITLE b900.
  SELECT-OPTIONS s_matnr FOR vbap-matnr.
  SELECT-OPTIONS s_werks FOR vbap-werks.
  SELECT-OPTIONS s_lgort FOR vbap-lgort.
  SELECT-OPTIONS s_charg FOR vbap-charg.
*  SELECT-OPTIONS s_wbs FOR vbap-ps_psp_pnr.
  SELECT-OPTIONS s_wbs FOR prps-posid. " 用外码才能模糊查询
  SELECT-OPTIONS s_vkorg FOR vbak-vkorg.
  SELECT-OPTIONS s_vtweg FOR vbak-vtweg.
  SELECT-OPTIONS s_spart FOR vbak-spart.
  SELECT-OPTIONS s_kunnr FOR vbak-kunnr.
SELECTION-SCREEN END OF BLOCK b900.

" 销售信息
SELECTION-SCREEN BEGIN OF BLOCK b910 WITH FRAME TITLE b910.
  SELECT-OPTIONS s_vbnva FOR vbak-vbeln MODIF ID m10.
  SELECT-OPTIONS s_vbtva FOR vbak-vbtyp MODIF ID m10.
  SELECT-OPTIONS s_auart FOR vbak-auart MODIF ID m10.
SELECTION-SCREEN END OF BLOCK b910.

" 交货信息
SELECTION-SCREEN BEGIN OF BLOCK b920 WITH FRAME TITLE b920.
  SELECT-OPTIONS s_vbnvl FOR likp-vbeln MODIF ID m20.
  SELECT-OPTIONS s_vbtvl FOR likp-vbtyp MODIF ID m20.
  SELECT-OPTIONS s_lfart FOR likp-lfart MODIF ID m20.
  SELECT-OPTIONS s_wadat FOR likp-wadat_ist MODIF ID m20.
SELECTION-SCREEN END OF BLOCK b920.

" 发票信息
SELECTION-SCREEN BEGIN OF BLOCK b930 WITH FRAME TITLE b930.
  SELECT-OPTIONS s_vbnvf FOR vbrk-vbeln MODIF ID m30.
  SELECT-OPTIONS s_vbtvf FOR vbrk-vbtyp MODIF ID m30.
  SELECT-OPTIONS s_fkdat FOR vbrk-fkdat MODIF ID m30.
SELECTION-SCREEN END OF BLOCK b930.

*&---------------------------------------------------------------------*
*& INITIALIZATION
*&---------------------------------------------------------------------*
INITIALIZATION.
  PERFORM frm_sel_scr_init.

*&---------------------------------------------------------------------*
*& PBO
*&---------------------------------------------------------------------*
AT SELECTION-SCREEN OUTPUT.
  PERFORM frm_sel_scr_pbo.

*&---------------------------------------------------------------------*
*& PAI
*&---------------------------------------------------------------------*
AT SELECTION-SCREEN.
  PERFORM frm_sel_scr_pai.

*&---------------------------------------------------------------------*
*& Form frm_sel_scr_init
*&---------------------------------------------------------------------*
*& 屏幕初始化
*&---------------------------------------------------------------------*
FORM frm_sel_scr_init .

  %_cb1_%_app_%-text = '交货清单'.
  %_cb2_%_app_%-text = '服务开票清单'.

  b900 = '选择条件'.
  %_s_matnr_%_app_%-text = '物料'.
  %_s_werks_%_app_%-text = '工厂'.
  %_s_lgort_%_app_%-text = '库存地点'.
  %_s_charg_%_app_%-text = '批次'.
  %_s_wbs_%_app_%-text = 'WBS'.
  %_s_vkorg_%_app_%-text = '销售组织'.
  %_s_vtweg_%_app_%-text = '分销渠道'.
  %_s_spart_%_app_%-text = '产品组'.
  %_s_kunnr_%_app_%-text = '售达方'.

  b910 = '销售信息'.
  %_s_vbnva_%_app_%-text = '销售订单'.
  %_s_vbtva_%_app_%-text = '销售凭证类别'.
  %_s_auart_%_app_%-text = '销售凭证类型'.

  b920 = '交货信息'.
  %_s_vbnvl_%_app_%-text = '交货订单'.
  %_s_vbtvl_%_app_%-text = '交货凭证类别'.
  %_s_lfart_%_app_%-text = '交货类型'.
  %_s_wadat_%_app_%-text = '交货日期'.

  b930 = '发票信息'.
  %_s_vbnvf_%_app_%-text = '发票凭证'.
  %_s_vbtvf_%_app_%-text = '发票凭证类别'.
  %_s_fkdat_%_app_%-text = '开票日期'.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_sel_scr_pbo
*&---------------------------------------------------------------------*
*& 屏幕PBO
*&---------------------------------------------------------------------*
FORM frm_sel_scr_pbo .

  " 关闭全部分组项目,未分组会展示
  LOOP AT SCREEN.
    IF screen-group1 IS NOT INITIAL.
      screen-input = '0'.
      screen-invisible = '1'.
      MODIFY SCREEN.
    ENDIF.
  ENDLOOP.

  " 显示分组
  DEFINE _active_grp.
    LOOP AT SCREEN.
      IF screen-group1 = &1.
        screen-input = '1'.
        screen-invisible = '0'.
        MODIFY SCREEN.
      ENDIF.
    ENDLOOP.
  END-OF-DEFINITION.

  " 隐藏分组
  DEFINE _inactive_grp.
    LOOP AT SCREEN.
      IF screen-group1 = &1.
        screen-input = '0'.
        screen-invisible = '1'.
        MODIFY SCREEN.
      ENDIF.
    ENDLOOP.
  END-OF-DEFINITION.

  SET TITLEBAR 'TITLE' WITH '营销报表'.
  _active_grp 'M10'. " 销售信息
  _active_grp 'M20'. " 交货信息
  _active_grp 'M30'. " 发票信息

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_sel_scr_pai
*&---------------------------------------------------------------------*
*& 屏幕PAI
*&---------------------------------------------------------------------*
FORM frm_sel_scr_pai .

ENDFORM.
子例程
*&---------------------------------------------------------------------*
*& 包含               ZSDREPORT_FRM
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*& Form frm_main
*&---------------------------------------------------------------------*
*& 程序入口
*&---------------------------------------------------------------------*
FORM frm_main .

  PERFORM frm_get_auth.
  PERFORM frm_get_data.
  PERFORM frm_display TABLES gt_data.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_get_auth
*&---------------------------------------------------------------------*
*& 权限相关
*&---------------------------------------------------------------------*
FORM frm_get_auth .

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_get_data
*&---------------------------------------------------------------------*
*& 取值相关
*&---------------------------------------------------------------------*
FORM frm_get_data .

  " 销售交货查询,以交货单为中心,查询对应销售凭证和开票凭证
  IF cb1 = abap_true.
    PERFORM frm_get_data_vl.
  ENDIF.
  " 无交货(服务/借贷项等)发票查询,以销售订单为中心,查询对应发票
  IF cb2 = abap_true.
    PERFORM frm_get_data_non_vl.
  ENDIF.

  CHECK gt_data IS NOT INITIAL.

  SORT gt_data BY vbeln_va posnr_va
                  vbeln_vl posnr_vl
                  vbeln_vf posnr_vf.

  DELETE ADJACENT DUPLICATES FROM gt_data COMPARING vbeln_va posnr_va
                                                    vbeln_vl posnr_vl
                                                    vbeln_vf posnr_vf.

  PERFORM frm_fetch_va.
  PERFORM frm_fetch_vl.
  PERFORM frm_fetch_vf.
  PERFORM frm_fetch_vbkd.
  PERFORM frm_fetch_vbpa.
  PERFORM frm_fetch_amount.
  PERFORM frm_fetch_text.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_vl_intersection
*&---------------------------------------------------------------------*
*& 取交集
*&---------------------------------------------------------------------*
FORM frm_intersection
  TABLES et_result TYPE tt_sdkey
  USING VALUE(it_sdkey1) TYPE tt_sdkey
        VALUE(it_sdkey2) TYPE tt_sdkey.

  CLEAR et_result[].

  CHECK it_sdkey1[] IS NOT INITIAL.
  CHECK it_sdkey2[] IS NOT INITIAL.

  DATA ls_sdkey TYPE ty_sdkey.

  IF lines( it_sdkey1 ) < lines( it_sdkey2 ).
    SORT it_sdkey2 BY vbeln posnr.
    LOOP AT it_sdkey1 INTO ls_sdkey.
      READ TABLE it_sdkey2 TRANSPORTING NO FIELDS WITH KEY
      vbeln = ls_sdkey-vbeln
      posnr = ls_sdkey-posnr
      BINARY SEARCH.
      IF sy-subrc = 0.
        INSERT ls_sdkey INTO TABLE et_result.
      ENDIF.
    ENDLOOP.
  ELSE.
    SORT it_sdkey1 BY vbeln posnr.
    LOOP AT it_sdkey2 INTO ls_sdkey.
      READ TABLE it_sdkey1 TRANSPORTING NO FIELDS WITH KEY
      vbeln = ls_sdkey-vbeln
      posnr = ls_sdkey-posnr
      BINARY SEARCH.
      IF sy-subrc = 0.
        INSERT ls_sdkey INTO TABLE et_result.
      ENDIF.
    ENDLOOP.
  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_filter_cancel_billing
*&---------------------------------------------------------------------*
*& 排除冲销和被冲销发票
*&---------------------------------------------------------------------*
FORM frm_filter_cancel_billing CHANGING ct_vf TYPE tt_sfakn.

  CHECK ct_vf[] IS NOT INITIAL.

  DATA(lt_cancel) = ct_vf[].
  SORT lt_cancel BY sfakn.
  LOOP AT ct_vf REFERENCE INTO DATA(lr_vf) WHERE sfakn IS INITIAL.
    READ TABLE lt_cancel INTO DATA(ls_cancel) WITH KEY sfakn = lr_vf->vbeln_vf BINARY SEARCH.
    IF sy-subrc = 0.
      lr_vf->sfakn = ls_cancel-vbeln_vf.
    ENDIF.
  ENDLOOP.
  CLEAR lt_cancel.
  DELETE ct_vf WHERE sfakn IS NOT INITIAL.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_get_data_vl
*&---------------------------------------------------------------------*
*& 销售交货查询,以交货单为中心,查询对应销售凭证和开票凭证
*&---------------------------------------------------------------------*
FORM frm_get_data_vl .

  CLEAR gs_preload.
  CLEAR gt_sdkey.

  PERFORM frm_preload_vl_from_vl.
  PERFORM frm_preload_vl_from_va.
  PERFORM frm_preload_vl_from_vf.

  IF gs_preload IS NOT INITIAL.
    " 预查询
    IF gt_sdkey IS NOT INITIAL.
      SELECT
        vbfa~vbelv AS vbeln_va,
        vbfa~posnv AS posnr_va,
        vbfa~vbeln AS vbeln_vl,
        vbfa~posnn AS posnr_vl
        FROM vbfa
        JOIN vbak ON vbfa~vbelv = vbak~vbeln
        JOIN likp ON vbfa~vbeln = likp~vbeln
        FOR ALL ENTRIES IN @gt_sdkey
        WHERE vbfa~vbeln = @gt_sdkey-vbeln
          AND vbfa~posnn = @gt_sdkey-posnr
        INTO TABLE @DATA(lt_vlva).
    ENDIF.
  ELSE.
    " 全量查询
    SELECT
      vbfa~vbelv AS vbeln_va,
      vbfa~posnv AS posnr_va,
      vbfa~vbeln AS vbeln_vl,
      vbfa~posnn AS posnr_vl
      FROM vbfa
      JOIN vbak ON vbfa~vbelv = vbak~vbeln
      JOIN likp ON vbfa~vbeln = likp~vbeln
      INTO TABLE @lt_vlva.
  ENDIF.

  CHECK lt_vlva IS NOT INITIAL.

  " 询报价/合同->销售,需要过滤这部分
  SELECT
    vbfa~vbelv AS vbeln_other,
    vbfa~posnv AS posnr_other,
    vbfa~vbeln AS vbeln_va,
    vbfa~posnn AS posnr_va
    FROM vbfa
    JOIN vbak AS other ON vbfa~vbelv = other~vbeln
    JOIN vbak AS va ON vbfa~vbeln = va~vbeln
    FOR ALL ENTRIES IN @lt_vlva
    WHERE vbfa~vbelv = @lt_vlva-vbeln_va
      AND vbfa~posnv = @lt_vlva-posnr_va
    INTO TABLE @DATA(lt_otherva).

  " 交货-发票
  IF lt_vlva IS NOT INITIAL.
    DATA lt_vlvf TYPE tt_sfakn.
    SELECT
      vbfa~vbelv AS vbeln_vl,
      vbfa~posnv AS posnr_vl,
      vbfa~vbeln AS vbeln_vf,
      vbfa~posnn AS posnr_vf,
      vbrk~sfakn
      FROM vbfa
      JOIN likp ON vbfa~vbelv = likp~vbeln
      JOIN vbrk ON vbfa~vbeln = vbrk~vbeln
      FOR ALL ENTRIES IN @lt_vlva
      WHERE vbfa~vbelv = @lt_vlva-vbeln_vl
        AND vbfa~posnv = @lt_vlva-posnr_vl
      INTO CORRESPONDING FIELDS OF TABLE @lt_vlvf.
    PERFORM frm_filter_cancel_billing CHANGING lt_vlvf. " 排除冲销和被冲销发票
  ENDIF.

  " 销售->发票
  " 对于退货交货,交货单后续无发票,需要根据销售订单查找
  DATA lt_vavf TYPE tt_sfakn.
  SELECT
    vbfa~vbelv AS vbeln_va,
    vbfa~posnv AS posnr_va,
    vbfa~vbeln AS vbeln_vf,
    vbfa~posnn AS posnr_vf,
    vbrk~sfakn " 冲销凭证
    FROM vbfa
    JOIN vbak ON vbfa~vbelv = vbak~vbeln
    JOIN vbrk ON vbfa~vbeln = vbrk~vbeln
    FOR ALL ENTRIES IN @lt_vlva
    WHERE vbfa~vbelv = @lt_vlva-vbeln_va
      AND vbfa~posnv = @lt_vlva-posnr_va
    INTO CORRESPONDING FIELDS OF TABLE @lt_vavf.
  PERFORM frm_filter_cancel_billing CHANGING lt_vavf. " 排除冲销和被冲销发票

  SORT lt_otherva BY vbeln_other posnr_other.
  SORT lt_vlvf BY vbeln_vl posnr_vl.
  SORT lt_vavf BY vbeln_va posnr_va.

  LOOP AT lt_vlva INTO DATA(ls_vlva).
    READ TABLE lt_otherva TRANSPORTING NO FIELDS WITH KEY
    vbeln_other = ls_vlva-vbeln_va
    posnr_other = ls_vlva-posnr_va
    BINARY SEARCH.
    IF sy-subrc = 0.
      CONTINUE.
    ENDIF.

    DATA ls_data TYPE ty_data.
    CLEAR ls_data.
    ls_data-vbeln_va = ls_vlva-vbeln_va.
    ls_data-posnr_va = ls_vlva-posnr_va.
    ls_data-vbeln_vl = ls_vlva-vbeln_vl.
    ls_data-posnr_vl = ls_vlva-posnr_vl.

    READ TABLE lt_vlvf INTO DATA(ls_vlvf) WITH KEY
    vbeln_vl = ls_data-vbeln_vl
    posnr_vl = ls_data-posnr_vl
    BINARY SEARCH.
    IF sy-subrc = 0.
      ls_data-vbeln_vf = ls_vlvf-vbeln_vf.
      ls_data-posnr_vf = ls_vlvf-posnr_vf.
    ELSE.
      READ TABLE lt_vavf INTO DATA(ls_vavf) WITH KEY
      vbeln_va = ls_data-vbeln_va
      posnr_va = ls_data-posnr_va
      BINARY SEARCH.
      IF sy-subrc = 0.
        ls_data-vbeln_vf = ls_vavf-vbeln_vf.
        ls_data-posnr_vf = ls_vavf-posnr_vf.
      ENDIF.
    ENDIF.

    INSERT ls_data INTO TABLE gt_data.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_preload_vl_from_vl
*&---------------------------------------------------------------------*
*& 直接按交货单查询
*&---------------------------------------------------------------------*
FORM frm_preload_vl_from_vl .

  IF s_vbnvl[] IS NOT INITIAL
  OR s_vbtvl[] IS NOT INITIAL
  OR s_lfart[] IS NOT INITIAL
  OR s_wadat[] IS NOT INITIAL
  OR s_matnr[] IS NOT INITIAL
  OR s_werks[] IS NOT INITIAL
  OR s_lgort[] IS NOT INITIAL
  OR s_charg[] IS NOT INITIAL
  OR s_wbs[]   IS NOT INITIAL
  OR s_kunnr[] IS NOT INITIAL
    .

    SELECT
      lips~vbeln,
      lips~posnr
      FROM likp
      JOIN lips ON likp~vbeln = lips~vbeln
      LEFT JOIN prps ON lips~ps_psp_pnr = prps~pspnr
      WHERE lips~vbeln IN @s_vbnvl
        AND likp~vbtyp IN @s_vbtvl
        AND likp~lfart IN @s_lfart
        AND likp~wadat IN @s_wadat
        AND likp~kunnr IN @s_kunnr
        AND lips~matnr IN @s_matnr
        AND lips~werks IN @s_werks
        AND lips~lgort IN @s_lgort
        AND lips~charg IN @s_charg
        AND prps~posid IN @s_wbs
      INTO TABLE @gt_sdkey.

    gs_preload-vl = abap_true. " 表示已执行预加载

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_preload_vl_from_va
*&---------------------------------------------------------------------*
*& 根据销售订单查找交货单
*&---------------------------------------------------------------------*
FORM frm_preload_vl_from_va .

  IF s_vbnva[] IS NOT INITIAL
  OR s_vbtva[] IS NOT INITIAL
  OR s_auart[] IS NOT INITIAL
  .

    DATA lt_sdkey TYPE tt_sdkey.
    SELECT
      vbfa~vbeln AS vbeln,
      vbfa~posnn AS posnr
      FROM vbfa
      JOIN vbak ON vbfa~vbelv = vbak~vbeln
      JOIN likp ON vbfa~vbeln = likp~vbeln
      WHERE vbak~vbeln IN @s_vbnva
        AND vbak~vbtyp IN @s_vbtva
        AND vbak~auart IN @s_auart
      INTO TABLE @lt_sdkey.

    " 将当前查询结果和之前的查询结果取交集
    IF gs_preload IS INITIAL.
      gt_sdkey = lt_sdkey.
    ELSE.
      PERFORM frm_intersection TABLES gt_sdkey USING gt_sdkey lt_sdkey.
    ENDIF.
    gs_preload-va = abap_true. " 表示已执行预加载

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_preload_vl_from_vf
*&---------------------------------------------------------------------*
*& 根据发票查找交货单
*&---------------------------------------------------------------------*
FORM frm_preload_vl_from_vf .

  IF s_vbnvf[] IS NOT INITIAL
  OR s_vbtvf[] IS NOT INITIAL
  OR s_fkdat[] IS NOT INITIAL
  .

    DATA lt_sdkey TYPE tt_sdkey.

    " 对于发票,先查找发票对应销售单,再根据销售单查找交货单
    " 这样一个流程,可以兼容退货的情况(对于退货,交货订单后续是物料凭证)
    DATA lt_vavf TYPE tt_sfakn.
    SELECT
      vbfa~vbelv AS vbeln_va,
      vbfa~posnv AS posnr_va,
      vbfa~vbeln AS vbeln_vf,
      vbfa~posnn AS posnr_vf,
      vbrk~sfakn " 冲销凭证
      FROM vbfa
      JOIN vbak ON vbfa~vbelv = vbak~vbeln
      JOIN vbrk ON vbfa~vbeln = vbrk~vbeln
      WHERE vbrk~vbeln IN @s_vbnvf
        AND vbrk~vbtyp IN @s_vbtvf
        AND vbrk~fkdat IN @s_fkdat
      INTO CORRESPONDING FIELDS OF TABLE @lt_vavf.
    PERFORM frm_filter_cancel_billing CHANGING lt_vavf. " 排除冲销和被冲销发票

    IF lt_vavf IS NOT INITIAL.
      SELECT
        vbfa~vbeln AS vbeln,
        vbfa~posnn AS posnr
        FROM vbfa
        JOIN vbak ON vbfa~vbelv = vbak~vbeln
        JOIN likp ON vbfa~vbeln = likp~vbeln
        FOR ALL ENTRIES IN @lt_vavf
        WHERE vbfa~vbelv = @lt_vavf-vbeln_va
          AND vbfa~posnv = @lt_vavf-posnr_va
        INTO TABLE @lt_sdkey.
    ENDIF.

    " 将当前查询结果和之前的查询结果取交集
    IF gs_preload IS INITIAL.
      gt_sdkey = lt_sdkey.
    ELSE.
      PERFORM frm_intersection TABLES gt_sdkey USING gt_sdkey lt_sdkey.
    ENDIF.
    gs_preload-vf = abap_true. " 表示有执行优化
  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_get_data_non_vl
*&---------------------------------------------------------------------*
*& 无交货(服务/借贷项等)发票查询,以销售订单为中心,查询对应发票
*&---------------------------------------------------------------------*
FORM frm_get_data_non_vl .

  CLEAR gs_preload.
  CLEAR gt_sdkey.

  PERFORM frm_preload_va_from_va.
  PERFORM frm_preload_va_from_vl.
  PERFORM frm_preload_va_from_vf.

  DATA lt_vavf TYPE tt_sfakn.

  IF gs_preload IS NOT INITIAL.
    " 预查询
    IF gt_sdkey IS NOT INITIAL.
      SELECT
        vbfa~vbelv AS vbeln_va,
        vbfa~posnv AS posnr_va,
        vbfa~vbeln AS vbeln_vf,
        vbfa~posnn AS posnr_vf,
        vbrk~sfakn " 冲销凭证
        FROM vbfa
        JOIN vbak ON vbfa~vbelv = vbak~vbeln
        JOIN vbrk ON vbfa~vbeln = vbrk~vbeln
        FOR ALL ENTRIES IN @gt_sdkey
        WHERE vbfa~vbelv = @gt_sdkey-vbeln
          AND vbfa~posnv = @gt_sdkey-posnr
        INTO CORRESPONDING FIELDS OF TABLE @lt_vavf.
    ENDIF.
  ELSE.
    " 全量查询
    SELECT
      vbfa~vbelv AS vbeln_va,
      vbfa~posnv AS posnr_va,
      vbfa~vbeln AS vbeln_vf,
      vbfa~posnn AS posnr_vf,
      vbrk~sfakn " 冲销凭证
      FROM vbfa
      JOIN vbak ON vbfa~vbelv = vbak~vbeln
      JOIN vbrk ON vbfa~vbeln = vbrk~vbeln
      INTO CORRESPONDING FIELDS OF TABLE @lt_vavf.
  ENDIF.
  PERFORM frm_filter_cancel_billing CHANGING lt_vavf. " 排除冲销和被冲销发票

  " 无交货判断
  IF lt_vavf IS NOT INITIAL.
    SELECT
      vbfa~vbelv AS vbeln_va,
      vbfa~posnv AS posnr_va,
      vbfa~vbeln AS vbeln_vl,
      vbfa~posnn AS posnr_vl
      FROM vbfa
      JOIN vbak ON vbfa~vbelv = vbak~vbeln
      JOIN likp ON vbfa~vbeln = likp~vbeln
      FOR ALL ENTRIES IN @lt_vavf
      WHERE vbfa~vbelv = @lt_vavf-vbeln_va
        AND vbfa~posnv = @lt_vavf-posnr_va
      INTO TABLE @DATA(lt_vavl).
  ENDIF.

  SORT lt_vavl BY vbeln_va posnr_va.

  LOOP AT lt_vavf INTO DATA(ls_vavf).
    READ TABLE lt_vavl TRANSPORTING NO FIELDS WITH KEY
    vbeln_va = ls_vavf-vbeln_va
    posnr_va = ls_vavf-posnr_va
    BINARY SEARCH.
    IF sy-subrc <> 0.
      INSERT CORRESPONDING #( ls_vavf ) INTO TABLE gt_data.
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_preload_vl_from_vl
*&---------------------------------------------------------------------*
*& 直接按交货单查询
*&---------------------------------------------------------------------*
FORM frm_preload_va_from_va.

  IF s_vbnva[] IS NOT INITIAL
  OR s_vbtva[] IS NOT INITIAL
  OR s_auart[] IS NOT INITIAL
  OR s_matnr[] IS NOT INITIAL
  OR s_werks[] IS NOT INITIAL
  OR s_lgort[] IS NOT INITIAL
  OR s_charg[] IS NOT INITIAL
  OR s_wbs[]   IS NOT INITIAL
  OR s_vkorg[] IS NOT INITIAL
  OR s_vtweg[] IS NOT INITIAL
  OR s_spart[] IS NOT INITIAL
  OR s_kunnr[] IS NOT INITIAL
    .

    SELECT
      vbap~vbeln,
      vbap~posnr
      FROM vbak
      JOIN vbap ON vbak~vbeln = vbap~vbeln
      LEFT JOIN prps ON vbap~ps_psp_pnr = prps~pspnr
      WHERE vbak~vbeln IN @s_vbnva
        AND vbak~vbtyp IN @s_vbtva
        AND vbak~auart IN @s_auart
        AND vbak~vkorg IN @s_vkorg
        AND vbak~vtweg IN @s_vtweg
        AND vbak~kunnr IN @s_kunnr
        AND vbap~spart IN @s_spart
        AND vbap~matnr IN @s_matnr
        AND vbap~werks IN @s_werks
        AND vbap~lgort IN @s_lgort
        AND vbap~charg IN @s_charg
        AND prps~posid IN @s_wbs
      INTO TABLE @gt_sdkey.

    gs_preload-va = abap_true. " 表示已执行预加载

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_preload_va_from_vl
*&---------------------------------------------------------------------*
*& 不存在这种情况,但假设屏幕有输入交货相关
*&---------------------------------------------------------------------*
FORM frm_preload_va_from_vl .

  IF s_vbnvl[] IS NOT INITIAL
  OR s_vbtvl[] IS NOT INITIAL
  OR s_lfart[] IS NOT INITIAL
  .

    CLEAR gt_sdkey.
    gs_preload-vl = abap_true. " 表示已执行预加载

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_preload_va_from_vf
*&---------------------------------------------------------------------*
*& 根据通过发票找销售订单
*&---------------------------------------------------------------------*
FORM frm_preload_va_from_vf.

  IF s_vbnvf[] IS NOT INITIAL
  OR s_vbtvf[] IS NOT INITIAL
  OR s_fkdat[] IS NOT INITIAL
  .

    DATA lt_vavf TYPE tt_sfakn.
    SELECT
      vbfa~vbelv AS vbeln_va,
      vbfa~posnv AS posnr_va,
      vbfa~vbeln AS vbeln_vf,
      vbfa~posnn AS posnr_vf,
      vbrk~sfakn " 冲销凭证
      FROM vbfa
      JOIN vbak ON vbfa~vbelv = vbak~vbeln
      JOIN vbrk ON vbfa~vbeln = vbrk~vbeln
      WHERE vbrk~vbeln IN @s_vbnvf
        AND vbrk~vbtyp IN @s_vbtvf
        AND vbrk~fkdat IN @s_fkdat
      INTO CORRESPONDING FIELDS OF TABLE @lt_vavf.
    PERFORM frm_filter_cancel_billing CHANGING lt_vavf." 排除冲销和被冲销发票

    " 无交货判断
    IF lt_vavf IS NOT INITIAL.
      SELECT
        vbfa~vbelv AS vbeln_va,
        vbfa~posnv AS posnr_va,
        vbfa~vbeln AS vbeln_vl,
        vbfa~posnn AS posnr_vl
        FROM vbfa
        JOIN vbak ON vbfa~vbelv = vbak~vbeln
        JOIN likp ON vbfa~vbeln = likp~vbeln
        FOR ALL ENTRIES IN @lt_vavf
        WHERE vbfa~vbeln = @lt_vavf-vbeln_va
          AND vbfa~posnn = @lt_vavf-posnr_va
        INTO TABLE @DATA(lt_vavl).
    ENDIF.

    SORT lt_vavl BY vbeln_va posnr_va.

    DATA lt_sdkey TYPE tt_sdkey.
    DATA ls_sdkey TYPE ty_sdkey.
    LOOP AT lt_vavf INTO DATA(ls_vavf).
      READ TABLE lt_vavl TRANSPORTING NO FIELDS WITH KEY
      vbeln_va = ls_vavf-vbeln_va
      posnr_va = ls_vavf-posnr_va
      BINARY SEARCH.
      IF sy-subrc <> 0.
        CLEAR ls_sdkey.
        ls_sdkey-vbeln = ls_vavf-vbeln_va.
        ls_sdkey-posnr = ls_vavf-posnr_va.
        INSERT ls_sdkey INTO TABLE lt_sdkey.
      ENDIF.
    ENDLOOP.

    " 将当前查询结果和之前的查询结果取交集
    IF gs_preload IS INITIAL.
      gt_sdkey = lt_sdkey.
    ELSE.
      PERFORM frm_intersection TABLES gt_sdkey USING gt_sdkey lt_sdkey.
    ENDIF.
    gs_preload-vf = abap_true. " 表示有执行优化

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_fetch_va
*&---------------------------------------------------------------------*
*& 读取销售信息
*&---------------------------------------------------------------------*
FORM frm_fetch_va .
  CHECK gt_data IS NOT INITIAL.

  SELECT
    vbap~vbeln,
    vbap~posnr,
    vbak~vbtyp, " 凭证类型
    vbak~auart, " 销售订单类型
    vbak~vkorg, " 销售组织
    vbak~vtweg, " 分销渠道
    vbak~spart, " 产品组
    vbap~waerk, " 货币
    vbap~kzwi1, " 销售小计
    vbap~kwmeng, " 订单数量
    vbap~zmeng, " 目标数量
    vbak~knumv, " 条件号
    vbap~meins, " 单位
    vbap~matnr, " 物料
    vbap~werks, " 工厂
    vbap~lgort, " 库存地点
    prps~pspnr, " WBS
    prps~posid, " WBS
    vbap~charg " 批次
    FROM vbak
    JOIN vbap ON vbak~vbeln = vbap~vbeln
    LEFT JOIN prps ON vbap~ps_psp_pnr = prps~pspnr
    FOR ALL ENTRIES IN @gt_data
    WHERE vbak~vbeln = @gt_data-vbeln_va
    INTO TABLE @DATA(lt_va).
  SORT lt_va BY vbeln posnr.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    READ TABLE lt_va INTO DATA(ls_va) WITH KEY
    vbeln = lr_data->vbeln_va
    posnr = lr_data->posnr_va
    BINARY SEARCH.
    IF sy-subrc = 0.
      lr_data->va = CORRESPONDING #( ls_va ).
      lr_data->matnr = ls_va-matnr.
      lr_data->werks = ls_va-werks.
      lr_data->lgort = ls_va-lgort.
      lr_data->pspnr = ls_va-pspnr.
      lr_data->posid = ls_va-posid.
      lr_data->charg = ls_va-charg.
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_fetch_va
*&---------------------------------------------------------------------*
*& 读取交货单信息
*&---------------------------------------------------------------------*
FORM frm_fetch_vl .
  CHECK gt_data IS NOT INITIAL.

  SELECT
    lips~vbeln, " 交货订单
    lips~posnr, " 行项目
    likp~vbtyp, " 凭证类型
    likp~lfart, " 交货类型
    likp~wadat_ist, " 实际交货日期
    lips~uecha, " 上层行(判断行项目是否为拆分行)
    lips~wbsta, " 处理状态
    lips~lfimg, " 交货数量
    lips~meins, " 单位
    lips~matnr, " 物料
    lips~werks, " 工厂
    lips~lgort, " 库存地点
    prps~pspnr, " WBS
    prps~posid, " WBS
    lips~charg " 批次
    FROM likp
    JOIN lips ON likp~vbeln = lips~vbeln
    LEFT JOIN prps ON lips~ps_psp_pnr = prps~pspnr
    FOR ALL ENTRIES IN @gt_data
    WHERE likp~vbeln = @gt_data-vbeln_vl
    INTO TABLE @DATA(lt_vl).
  SORT lt_vl BY vbeln posnr.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    READ TABLE lt_vl INTO DATA(ls_vl) WITH KEY
    vbeln = lr_data->vbeln_vl
    posnr = lr_data->posnr_vl
    BINARY SEARCH.
    IF sy-subrc = 0.
      lr_data->vl = CORRESPONDING #( ls_vl ).
      lr_data->matnr = ls_vl-matnr.
      lr_data->werks = ls_vl-werks.
      lr_data->lgort = ls_vl-lgort.
      lr_data->pspnr = ls_vl-pspnr.
      lr_data->posid = ls_vl-posid.
      lr_data->charg = ls_vl-charg.
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_fetch_vf
*&---------------------------------------------------------------------*
*& 读取发票信息
*&---------------------------------------------------------------------*
FORM frm_fetch_vf .
  CHECK gt_data IS NOT INITIAL.

  SELECT
    vbrp~vbeln, " 发票
    vbrp~posnr, " 发票行
    vbrk~vbtyp, " 发票凭证类别
    vbrk~fkdat, " 开票日期
    vbrp~fkimg, " 开票数量
    vbrp~meins, " 单位
    vbrp~kzwi1, " 发票小计
    vbrp~waerk " 货币
    FROM vbrk
    JOIN vbrp ON vbrk~vbeln = vbrp~vbeln
    FOR ALL ENTRIES IN @gt_data
    WHERE vbrk~vbeln = @gt_data-vbeln_vf
    INTO TABLE @DATA(lt_vf).
  SORT lt_vf BY vbeln posnr.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    READ TABLE lt_vf INTO DATA(ls_vf) WITH KEY
    vbeln = lr_data->vbeln_vf
    posnr = lr_data->posnr_vf
    BINARY SEARCH.
    IF sy-subrc = 0.
      lr_data->vf = CORRESPONDING #( ls_vf ).
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_fetch_vbkd
*&---------------------------------------------------------------------*
*& 读取VBKD
*&---------------------------------------------------------------------*
FORM frm_fetch_vbkd .
  CHECK gt_data IS NOT INITIAL.

  SELECT
    vbeln,
    posnr,
    bstkd
    FROM vbkd
    FOR ALL ENTRIES IN @gt_data
    WHERE vbeln = @gt_data-vbeln_va
    INTO TABLE @DATA(lt_vbkd).
  SORT lt_vbkd BY vbeln posnr.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    READ TABLE lt_vbkd INTO DATA(ls_vbkd) WITH KEY
    vbeln = lr_data->vbeln_va
    BINARY SEARCH.
    IF sy-subrc = 0.
      lr_data->bstkd_va = ls_vbkd-bstkd.
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_fetch_vbpa
*&---------------------------------------------------------------------*
*& 销售业务货币
*&---------------------------------------------------------------------*
FORM frm_fetch_vbpa .
  CHECK gt_data IS NOT INITIAL.

  SELECT
    vbeln,
    parvw,
    kunnr
    FROM vbpa
    FOR ALL ENTRIES IN @gt_data
    WHERE vbeln = @gt_data-vbeln_va
      AND posnr = '' " 取抬头业务伙伴
    INTO TABLE @DATA(lt_vbpa).

  SORT lt_vbpa BY vbeln parvw.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).

    " 重复代码
    DATA ls_vbpa LIKE LINE OF lt_vbpa.
    DEFINE _get_partner.
      CLEAR ls_vbpa.
      READ TABLE lt_vbpa INTO ls_vbpa WITH KEY
      vbeln = lr_data->vbeln_va
      parvw = &1
      BINARY SEARCH.
      IF sy-subrc = 0.
        &2 = ls_vbpa-kunnr.
      ENDIF.
    END-OF-DEFINITION.

    _get_partner 'AG' lr_data->kunag.
    _get_partner 'RE' lr_data->kunre.
    _get_partner 'RG' lr_data->kunrg.

  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_fetch_amount
*&---------------------------------------------------------------------*
*& 数量与金额处理
*&---------------------------------------------------------------------*
FORM frm_fetch_amount .
  CHECK gt_data IS NOT INITIAL.

  SELECT
    knumv,
    kposn,
    stunr,
    zaehk,
    kschl,
    waers,
    kbetr,
    kpein
    FROM prcd_elements
    FOR ALL ENTRIES IN @gt_data
    WHERE knumv = @gt_data-knumv_va
      AND kinak = '' " 排除未激活的定价
    INTO TABLE @DATA(lt_prcd_elements).

  " 对于多个价格,取最新的
  SORT lt_prcd_elements BY knumv kposn kschl
                           zaehk DESCENDING .
  DELETE ADJACENT DUPLICATES FROM lt_prcd_elements COMPARING knumv kposn kschl.

  " 对于下面类型,数量金额按负数处理
  DATA lt_vbtyp_neg_opt TYPE RANGE OF vbtyp.
  lt_vbtyp_neg_opt = VALUE #( sign = 'I' option = 'EQ'
    ( low = 'H' ) " 退货
    ( low = 'T' ) " 订单的退货交货
    ( low = 'K' ) " 贷项凭证申请
    ( low = 'O' ) " 贷项凭证
  ).

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    " 重复代码
    DATA ls_prcd_elements LIKE LINE OF lt_prcd_elements.
    DEFINE _get_prcd_elements.
      READ TABLE lt_prcd_elements INTO ls_prcd_elements WITH KEY
      knumv = lr_data->knumv_va
      kposn = lr_data->posnr_va
      kschl = &1
      BINARY SEARCH.
      IF sy-subrc <> 0.
        CLEAR ls_prcd_elements.
      ENDIF.
    END-OF-DEFINITION.

    " 判断是否为退货之类
    IF lr_data->vbtyp_va IN lt_vbtyp_neg_opt
    OR lr_data->vbtyp_vl IN lt_vbtyp_neg_opt
    OR lr_data->vbtyp_vf IN lt_vbtyp_neg_opt.
      lr_data->zfactor = -1.
    ELSE.
      lr_data->zfactor = 1.
    ENDIF.

    " 数量金额方向校准
    lr_data->kwmeng_va *= lr_data->zfactor.
    lr_data->zmeng_va *= lr_data->zfactor.
    lr_data->kzwi1_va *= lr_data->zfactor.
    lr_data->lfimg_vl *= lr_data->zfactor.
    lr_data->fkimg_vf *= lr_data->zfactor.
    lr_data->kzwi1_vf *= lr_data->zfactor.

    " 税率
    _get_prcd_elements 'MWSI'.
    lr_data->ztax_rate = ls_prcd_elements-kbetr / 100. " 底表记录13,转换为0.13

    " 销售数量,对于订单,取订单数量,对于和同或服务框架之类的,取目标数量
    IF lr_data->kwmeng_va <> 0.
      lr_data->zquan2 = lr_data->kwmeng_va.
    ELSEIF lr_data->zmeng_va <> 0.
      lr_data->zquan2 = lr_data->zmeng_va.
    ENDIF.
    " 销售单价 = 数量(订单数量或目标数量) / 小计
    " 看项目实际需求,可能会取定价值,这边简单取小计值即可
    IF lr_data->zquan2 <> 0.
      lr_data->zamount1 = lr_data->kzwi1_va / lr_data->zquan2.
    ENDIF.
    " 销售含税总金额 = 小计
    lr_data->zamount3 = lr_data->kzwi1_va.

    IF lr_data->vbeln_vl IS NOT INITIAL.
      lr_data->zamount4 = lr_data->zamount1. " 交货单价 = 销售单价
      " 根据行项目过账状态判断交货数量,不会有中间状态
      IF lr_data->wbsta_vl = 'C'. " 完全交货
        lr_data->zquan5 = lr_data->lfimg_vl. " 交货数量
      ENDIF.
      lr_data->zamount6 = lr_data->zquan5 * lr_data->zamount4. " 交货含税总金额 = 交货数量 * 交货单价
      lr_data->zquan7 = lr_data->zquan2 - lr_data->zquan5. " 未交货数量 = 销售数量 - 交货数量
      lr_data->zamount8 = lr_data->zquan7 * lr_data->zamount4. " 未交货含税总金额 = 未交货数量 * 交货单价
    ENDIF.

    " 开票单价 = 数量 / 小计
    IF lr_data->fkimg_vf <> 0.
      lr_data->zamount9 = lr_data->kzwi1_vf / lr_data->fkimg_vf.
    ENDIF.
    lr_data->zquan10 = lr_data->lfimg_vl. " 开票数量
    lr_data->zamount11 = lr_data->zquan10 * lr_data->zamount9. " 开票含税总金额 = 开票数量 * 开票单价
    IF lr_data->vbeln_vl IS NOT INITIAL.
      lr_data->zquan12 = lr_data->zquan5 - lr_data->lfimg_vl. " 未开票数量 = 交货数量 - 开票数量
    ELSE.
      lr_data->zquan12 = lr_data->zquan2 - lr_data->lfimg_vl. " 未开票数量 = 销售数量 - 开票数量
    ENDIF.
    lr_data->zamount13 = lr_data->zquan12 * lr_data->zamount11. " 未交货含税总金额 = 未交货数量 * 交货单价
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_fetch_text
*&---------------------------------------------------------------------*
*& 读取文本
*&---------------------------------------------------------------------*
FORM frm_fetch_text .
  CHECK gt_data IS NOT INITIAL.

*  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
*    lr_data->kunag_text = zcl_text=>auto( lr_data->kunag ).
*    lr_data->kunnr_text = zcl_text=>auto( lr_data->kunnr ).
*    lr_data->kunre_text = zcl_text=>auto( lr_data->kunre ).
*    lr_data->kunrg_text = zcl_text=>auto( lr_data->kunrg ).
*    lr_data->maktx = zcl_text=>auto( lr_data->matnr ).
*  ENDLOOP.

ENDFORM.
ALV
*&---------------------------------------------------------------------*
*& 包含               ZSDREPORT_ALV
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*& Form frm_display
*&---------------------------------------------------------------------*
*& 报表展示
*&---------------------------------------------------------------------*
FORM frm_display TABLES ct_data TYPE STANDARD TABLE.

  DATA ls_layout TYPE lvc_s_layo.
  DATA lt_fieldcat TYPE STANDARD TABLE OF lvc_s_fcat.

  PERFORM frm_set_layout CHANGING ls_layout.
  PERFORM frm_set_fieldcat TABLES lt_fieldcat.
  PERFORM frm_set_style.
  PERFORM frm_set_color.

  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
    EXPORTING
      i_callback_program      = sy-repid
*     i_callback_pf_status_set = 'FRM_PF_STATUS'
      i_callback_user_command = 'FRM_USER_COMMAND'
      is_layout_lvc           = ls_layout
      it_fieldcat_lvc         = lt_fieldcat
      i_default               = abap_true
      i_save                  = 'A'
    TABLES
      t_outtab                = ct_data[]
    EXCEPTIONS
      program_error           = 1
      OTHERS                  = 2.
  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_set_layout
*&---------------------------------------------------------------------*
*& 报表布局设置
*&---------------------------------------------------------------------*
FORM frm_set_layout CHANGING cs_layout TYPE lvc_s_layo.

  CLEAR cs_layout.
  cs_layout-zebra = abap_true. " 斑马线
  cs_layout-cwidth_opt = abap_true. " 自动调整ALVL列宽
  cs_layout-sel_mode = 'A'. " 选择模式
  cs_layout-ctab_fname = 'T_SCOL'. " 单元格颜色设置
  cs_layout-stylefname = 'T_STYL'. " 单元格控制

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_set_fieldcat
*&---------------------------------------------------------------------*
*& 字段目录设置
*&---------------------------------------------------------------------*
FORM frm_set_fieldcat TABLES ct_fieldcat TYPE lvc_t_fcat.

  DATA ls_fieldcat TYPE lvc_s_fcat.

  DEFINE _init_fieldcat.
    CLEAR ls_fieldcat.
    ls_fieldcat-fieldname = &1.
    ls_fieldcat-tooltip =
    ls_fieldcat-coltext =
    ls_fieldcat-seltext =
    ls_fieldcat-scrtext_l =
    ls_fieldcat-scrtext_m =
    ls_fieldcat-scrtext_s = &2.
    ls_fieldcat-ref_table = &3.
    ls_fieldcat-ref_field = &4.
    INSERT ls_fieldcat INTO TABLE ct_fieldcat.
  END-OF-DEFINITION.


  _init_fieldcat 'VBELN_VA' '销售订单' 'VBAP' 'VBELN'.
  _init_fieldcat 'POSNR_VA' '销售行项目' 'VBAP' 'POSNR'.
  _init_fieldcat 'VBTYP_VA ' '凭证类型' 'VBAK' 'VBTYP'.
  _init_fieldcat 'AUART_VA ' '销售订单类型' 'VBAK' 'AUART'.
  _init_fieldcat 'VKORG_VA ' '销售组织' 'VBAK' 'VKORG'.
  _init_fieldcat 'VTWEG_VA ' '分销渠道' 'VBAK' 'VTWEG'.
  _init_fieldcat 'SPART_VA ' '产品组' 'VBAK' 'SPART'.
*  _init_fieldcat 'KWMENG_VA' '订单数量' 'VBAP' 'KWMENG'.
*  _init_fieldcat 'ZMENG_VA ' '目标数量' 'VBAP' 'ZMENG'.
  _init_fieldcat 'MEINS_VA ' '单位' 'VBAP' 'MEINS'.
*  _init_fieldcat 'KZWI1_VA ' '销售小计' 'VBAP' 'KZWI1'.
  _init_fieldcat 'WAERK_VA ' '货币' 'VBAP' 'WAERK'.
  _init_fieldcat 'KNUMV_VA ' '条件号' 'VBAK' 'KNUMV'.
*  _init_fieldcat 'KBETR_VA ' '定价值' 'PRCD_ELEMENTS' 'KBETR'.
  _init_fieldcat 'BSTKD_VA ' '客户参考' 'VBKD' 'BSTKD'.

  _init_fieldcat 'KUNAG     ' '签署方编码' 'KNA1' 'KUNNR'.
  _init_fieldcat 'KUNAG_TEXT' '签署方名称' '' ''.
  _init_fieldcat 'KUNNR     ' '送达方编码' 'KNA1' 'KUNNR'.
  _init_fieldcat 'KUNNR_TEXT' '送达方名称' '' ''.
  _init_fieldcat 'KUNRE     ' '开票方编码' 'KNA1' 'KUNNR'.
  _init_fieldcat 'KUNRE_TEXT' '开票方名称' '' ''.
  _init_fieldcat 'KUNRG     ' '付款方编码' 'KNA1' 'KUNNR'.
  _init_fieldcat 'KUNRG_TEXT' '付款方名称' '' ''.

  _init_fieldcat 'MATNR' '物料号' 'MARA' 'MATNR'.
  _init_fieldcat 'MAKTX' '物料描述' 'MAKT' 'MAKTX'.
  _init_fieldcat 'WERKS' '工厂' 'T001W' 'WERKS'.
  _init_fieldcat 'LGORT' '库位' 'T001L' 'LGORT'.
  _init_fieldcat 'POSID' 'WBS' 'PRPS' 'POSID'.
  _init_fieldcat 'CHARG' '批次' 'MCH1' 'CHARG'.

  _init_fieldcat 'VBELN_VL' '交货订单' 'LIPS' 'VBELN'.
  _init_fieldcat 'POSNR_VL' '交货行项目' 'LIPS' 'POSNR'.
  _init_fieldcat 'VBTYP_VL' '销售和分销凭证类别' 'LIKP' 'VBTYP'.
  _init_fieldcat 'LFART_VL' '交货类型' 'LIKP' 'LFART'.
  _init_fieldcat 'WADAT_IST_VL' '实际交货日期' 'LIKP' 'WADAT_IST'.
  _init_fieldcat 'UECHA_VL' '上层项目批' 'LIPS' 'UECHA'.
  _init_fieldcat 'WBSTA_VL' '交货状态' 'LIPS' 'WBSTA'.
*  _INIT_FIELDCAT 'LFIMG_VL' '交货数量' 'LIPS' 'LFIMG'.
  _init_fieldcat 'MEINS_VL' '单位' 'LIPS' 'MEINS'.

  _init_fieldcat 'VBELN_VF' '开票凭证' 'VBRP' 'VBELN'.
  _init_fieldcat 'POSNR_VF' '开票行项目' 'VBRP' 'POSNR'.
  _init_fieldcat 'VBTYP_VF' '发票凭证类别' 'VBRK' 'VBTYP'.
  _init_fieldcat 'FKDAT_VF' '开票日期' 'VBRK' 'FKDAT'.
*  _INIT_FIELDCAT 'FKIMG_VF' '开票数量' 'VBRP' 'FKIMG'.
  _init_fieldcat 'MEINS_VF' '单位' 'VBRP' 'MEINS'.
*  _INIT_FIELDCAT 'KZWI1_VF' '发票小计' 'VBRP' 'KZWI1'.
  _init_fieldcat 'WAERK_VF' '货币' 'VBRP' 'WAERK'.

  _init_fieldcat 'ZTAX_RATE' '税率' '' ''.

  _init_fieldcat 'ZAMOUNT1 ' '销售单价' '' ''.
  _init_fieldcat 'ZQUAN2   ' '销售数量' '' ''.
  _init_fieldcat 'ZAMOUNT3 ' '销售含税总金额' '' ''.

  _init_fieldcat 'ZAMOUNT4 ' '发货单价' '' ''.
  _init_fieldcat 'ZQUAN5   ' '交货数量' '' ''.
  _init_fieldcat 'ZAMOUNT6 ' '发货含税总金额' '' ''.
  _init_fieldcat 'ZQUAN7   ' '未交货数量' '' ''.
  _init_fieldcat 'ZAMOUNT8 ' '未交货含税总金额' '' ''.

  _init_fieldcat 'ZAMOUNT9 ' '开票单价' '' ''.
  _init_fieldcat 'ZQUAN10  ' '开票数量' '' ''.
  _init_fieldcat 'ZAMOUNT11' '开票含税总金额' '' ''.
  _init_fieldcat 'ZQUAN12  ' '未开票数量' '' ''.
  _init_fieldcat 'ZAMOUNT13' '未开票含税总金额' '' ''.

  " 个性化自己输出数据格式
  LOOP AT ct_fieldcat REFERENCE INTO DATA(lr_fieldcat).
    IF lr_fieldcat->fieldname = 'ZSEL'.
      lr_fieldcat->checkbox = abap_true.
    ENDIF.

    " 销售参考
    IF lr_fieldcat->fieldname = 'KWMENG_VA' " 订单数量
    OR lr_fieldcat->fieldname = 'ZMENG_VA' " 目标数量
    OR lr_fieldcat->fieldname = 'ZQUAN2' " 销售数量
    .
      lr_fieldcat->qfieldname = 'MEINS_VA'.
    ENDIF.

    IF lr_fieldcat->fieldname = 'KZWI1_VA' " 小计
    OR lr_fieldcat->fieldname = 'KBETR_VA' " 定价值
    OR lr_fieldcat->fieldname = 'ZAMOUNT1' " 销售单价
    OR lr_fieldcat->fieldname = 'ZAMOUNT3' " 销售含税总金额
    .
      lr_fieldcat->qfieldname = 'WAERK_VA'.
    ENDIF.

    " 交货参考
    IF lr_fieldcat->fieldname = 'LFIMG_VL' " 交货数量
    OR lr_fieldcat->fieldname = 'ZQUAN5' " 交货数量
    OR lr_fieldcat->fieldname = 'ZQUAN7' " 未交货数量
    .
*      lr_fieldcat->qfieldname = 'MEINS_VL'.
      lr_fieldcat->qfieldname = 'MEINS_VA'.
    ENDIF.

    IF lr_fieldcat->fieldname = 'ZAMOUNT4' " 交货单价
    OR lr_fieldcat->fieldname = 'ZAMOUNT6' " 发货含税总金额
    OR lr_fieldcat->fieldname = 'ZAMOUNT7' " 未发货含税总金额
    .
*      lr_fieldcat->qfieldname = 'WAERK_VL'.
      lr_fieldcat->qfieldname = 'WAERK_VA'.
    ENDIF.

    " 开票参考
    IF lr_fieldcat->fieldname = 'FKIMG_VF' " 开票数量
    OR lr_fieldcat->fieldname = 'ZQUAN10' " 开票数量
    OR lr_fieldcat->fieldname = 'ZQUAN12' " 未开票数量
    .
      lr_fieldcat->qfieldname = 'MEINS_VF'.
    ENDIF.

    IF lr_fieldcat->fieldname = 'KZWI1_VF' " 开票小计
    OR lr_fieldcat->fieldname = 'ZAMOUNT9' " 开票单价
    OR lr_fieldcat->fieldname = 'ZAMOUNT11' " 开票含税总金额
    OR lr_fieldcat->fieldname = 'ZAMOUNT13' " 未开票含税总金额
    .
      lr_fieldcat->qfieldname = 'WAERK_VF'.
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_set_style
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
FORM frm_set_style.

  DATA ls_styl TYPE lvc_s_styl.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    CLEAR lr_data->t_styl.

    " 设置ALV字段或行的状态
    " 常用有禁止编辑CL_GUI_ALV_GRID=>MC_STYLE_DISABLED
    " 禁止删除行CL_GUI_ALV_GRID=>MC_STYLE_NO_DELETE_ROW

  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_set_color
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
FORM frm_set_color.

  STATICS:
    BEGIN OF color,
      r_light     TYPE char04,
      r_highlight TYPE char04,
      r_success   TYPE char04,
      r_error     TYPE char04,
      light       TYPE lvc_s_scol-color,
      highlight   TYPE lvc_s_scol-color,
      success     TYPE lvc_s_scol-color,
      error       TYPE lvc_s_scol-color,
    END OF color.
  IF color IS INITIAL.
    color-r_light = 'C300'.
    color-r_highlight = 'C310'.
    color-r_success = 'C500'.
    color-r_error = 'C600'.
    color-light = VALUE #( col = 3 ).
    color-highlight = VALUE #( col = 3 int = 1 ).
    color-success = VALUE #( col = 5 ).
    color-error = VALUE #( col = 6 ).
  ENDIF.

  DATA ls_scol TYPE lvc_s_scol.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    CLEAR lr_data->t_scol.

    " 关键信息高亮显示
    lr_data->t_scol = VALUE #( BASE lr_data->t_scol color = color-light " highlight
      ( fname = 'VBELN_VA    ' ) " 销售订单号
      ( fname = 'VBELN_VF    ' ) " 开票凭证
      ( fname = 'VBELN_VL    ' ) " 交货单号
    ).

    " 关键信息高亮显示
    lr_data->t_scol = VALUE #( BASE lr_data->t_scol color = color-light
      ( fname = 'KUNAG' ) " 签署方编码
      ( fname = 'KUNNR' ) " 送达方编码
      ( fname = 'KUNRE' ) " 开票方编码
      ( fname = 'KUNRG' ) " 付款方编码

      ( fname = 'MATNR' ) " 物料号
      ( fname = 'WERKS' ) " 工厂
      ( fname = 'LGORT' ) " 库位
      ( fname = 'PSPNR' ) " WBS
      ( fname = 'POSID' ) " WBS
      ( fname = 'CHARG' ) " SAP批次号

      ( fname = 'MEINS_VA' ) " 单位
      ( fname = 'MEINS_VL' ) " 单位
      ( fname = 'MEINS_VF' ) " 单位
      ( fname = 'WAERK_VA' ) " 货币
      ( fname = 'WAERK_VL' ) " 货币
      ( fname = 'WAERK_VF' ) " 货币

      ( fname = 'KWMENG_VA' ) " 订单数量
      ( fname = 'ZMENG_VA' ) " 目标数量
      ( fname = 'KZWI1_VA' ) " 销售小计
      ( fname = 'LFIMG_VL' ) " 发货数量
      ( fname = 'FKIMG_VF' ) " 开票数量
      ( fname = 'KZWI1_VF' ) " 发票小计

      ( fname = 'ZAMOUNT1 ' ) " 销售单价
      ( fname = 'ZQUAN2   ' ) " 销售数量
      ( fname = 'ZAMOUNT3 ' ) " 销售含税总金额

      ( fname = 'ZAMOUNT4 ' ) " 发货单价
      ( fname = 'ZQUAN5   ' ) " 交货数量
      ( fname = 'ZAMOUNT6 ' ) " 发货含税总金额
      ( fname = 'ZQUAN7   ' ) " 未交货数量
      ( fname = 'ZAMOUNT8 ' ) " 未交货含税总金额

      ( fname = 'ZAMOUNT9 ' ) " 开票单价
      ( fname = 'ZQUAN10  ' ) " 开票数量
      ( fname = 'ZAMOUNT11' ) " 开票含税总金额
      ( fname = 'ZQUAN12  ' ) " 未开票数量
      ( fname = 'ZAMOUNT13' ) " 未开票含税总金额
    ).

    IF lr_data->mtype = 'E'.
      lr_data->t_scol = VALUE #( BASE lr_data->t_scol color = color-error
        ( fname = 'MTYPE' )
        ( fname = 'MSG' )
      ).
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_pf_status
*&---------------------------------------------------------------------*
*& 设置GUI状态
*&---------------------------------------------------------------------*
FORM frm_pf_status USING ct_extab TYPE slis_t_extab.

  DATA lt_excluding TYPE STANDARD TABLE OF sy-ucomm.
  SET PF-STATUS 'STATUS' EXCLUDING lt_excluding.

  DATA l_title TYPE string.
  SET TITLEBAR 'TITLE' WITH l_title.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_user_command
*&---------------------------------------------------------------------*
*& 功能响应
*&---------------------------------------------------------------------*
FORM frm_user_command USING cv_ucomm LIKE sy-ucomm
                            cs_selfield TYPE slis_selfield.

  " 刷新屏幕数据到内表
  DATA: lo_grid TYPE REF TO cl_gui_alv_grid.
  CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
    IMPORTING
      e_grid = lo_grid.
  CALL METHOD lo_grid->check_changed_data.

  PERFORM frm_get_data_selection.

  " 按钮功能实现
  CASE cv_ucomm.
    WHEN '&IC1'. " 双击
      CASE cs_selfield-fieldname.
        WHEN 'MATNR'.
          SET PARAMETER ID 'MXX' FIELD 'K'.
          SET PARAMETER ID 'MAT' FIELD cs_selfield-value.
          CALL TRANSACTION 'MM03' AND SKIP FIRST SCREEN.

        WHEN 'VBELN_VA'.
          SET PARAMETER ID 'AUN' FIELD cs_selfield-value.
          CALL TRANSACTION 'VA03' AND SKIP FIRST SCREEN.

        WHEN 'VBELN_VL'.
          SET PARAMETER ID 'VL' FIELD cs_selfield-value.
          CALL TRANSACTION 'VL03N' AND SKIP FIRST SCREEN.

        WHEN 'VBELN_VF'.
          SET PARAMETER ID 'VF' FIELD cs_selfield-value.
          CALL TRANSACTION 'VF03' AND SKIP FIRST SCREEN.

      ENDCASE.

    WHEN OTHERS.
  ENDCASE.

  " 刷新ALV 显示值
  cs_selfield-refresh = abap_true .
  cs_selfield-row_stable = abap_true .
  cs_selfield-col_stable = abap_true .

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_get_data_selection
*&---------------------------------------------------------------------*
*& 传递侧边栏选择到ZSEL字段
*&---------------------------------------------------------------------*
FORM frm_get_data_selection.

  " 刷新屏幕数据到内表
  DATA: lo_grid TYPE REF TO cl_gui_alv_grid.
  CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
    IMPORTING
      e_grid = lo_grid.

  " 获取ALV选取行
  DATA lt_rows TYPE lvc_t_row.
  CALL METHOD lo_grid->get_selected_rows
    IMPORTING
      et_index_rows = lt_rows.

  LOOP AT lt_rows INTO DATA(ls_row).
    READ TABLE gt_data REFERENCE INTO DATA(lr_data) INDEX ls_row-index.
    IF sy-subrc = 0.
      lr_data->zsel = abap_true.
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_reset_data_selection
*&---------------------------------------------------------------------*
*& 取消全部勾选
*&---------------------------------------------------------------------*
FORM frm_reset_data_selection.

  LOOP AT gt_data REFERENCE INTO DATA(lr_data).
    CLEAR lr_data->zsel.
  ENDLOOP.

ENDFORM.