精华内容
下载资源
问答
  • 本文首先介绍了对账的概念、基本内容,其次讲解了对账系统中常见问题及解决方法,最后详细讲解了整个对账系统的流程设计、整体框架。本文所说的对账是一个通用概念,不针对具体行业,各应用领域可根据实际情况进行...

    转自: http://mp.weixin.qq.com/s/y2I_EYSLlq_239vZXr0OZQ

    本文首先介绍了对账的概念、基本内容,其次讲解了对账系统中常见问题及解决方法,最后详细讲解了整个对账系统的流程设计、整体框架。本文所说的对账是一个通用概念,不针对具体行业,各应用领域可根据实际情况进行调整。

    对账系统简介

    对账是金融领域中的名词,对应的学科为大家熟知的会计学。在金融领域中,不仅银行、基金、第三方支付机构需要对账,其他任何涉及金融交易的公司/机构都需要对账,比如商户业务、信贷业务等。

     

    对账是指对前一个清算周期的交易信息进行核对,以确认交易信息的一致性和正确性的过程。这是普遍认可的一个概念,可以用一句话总结:确保单个周期内,信息流和现金流的一致。

     

    通过对账可以保证账证一致、账账一致、账实一致,三者一致的正确、真实、完整为后续的佣金、分润等计算提供基础。对账的准确性也为系统、人工平账提供了差错信息,确保平账后达到财务一致。

     

    总之,对一个公司来说,通过对账,可以正确地反映企业的财务状态,及时发现差错,确保业务健康发展。

     

    对账大部分涉及两方对账,极少情况下会有三方对账的情况。三方对账本身的系统逻辑比较复杂,特别是三方平账时的差错处理更加复杂,这里不作讨论。

     

    对于没有虚拟账户,只有交易通道类的对账,有两种对账类型:总分对账和明细对账。

     

    总分对账即根据不同的交易通道,把每个交易通道的进出或者单独的交易类型进行汇总,按照不同交易通道、不同交易类型进行总对总对账,确保和每个交易通道的进出是一致的,确保每个通道不会有长款或短款的情况。

     

    对大部分系统来说,总分对账没有差错就不用进行明细对账了。

     

    如果发现总分对账有差错,就需要进行明细对账,将具体差错信息找出来。明细对账,顾名思义就是将发生的每一笔交易的详细信息和交易通道的对账文件进行逐笔核对,找出是否有不一致的交易。

     

     

    (图1 账户总余额连续性公式)

     

    对于有虚拟账户的对账,还需要每个清算日对账户金额进行核验检查。核验主要包括两部分:一是账户总余额金额连续性检查,如图1公式所示;二是记账准确性检查。

     

    对账常见问题及处

     

    (图2 对账链路示意图)

     

    对账一般都是以一方为对账基准进行轧账,出现差错时,通过各种差错交易。以基准方为准进行处理,最终达到平账。

     

    对账的系统顺序:首先是金融交易最底层的银行内部进行对账以及平账处理,处理完毕后出具对账文件;和银行直接对接的第三方支付机构依此对账文件进行对账轧账,发现差错时,进行平账处理;依次往上,直到真正业务场景的用户。

     

    从中可以发现,离用户越近的系统拿到对账文件的时间越晚,等最终整个业务场景链对完账,时间就比较晚了,比如 T+2、T+3,甚至更晚。典型的如消费金融公司,和支付渠道、合作方对完账,加上平账处理,基本T+2之后了,如图2所示。。

     

     

     

    (图3 清算周期时间切分点示意图)

     

    由于对账都是基于一个清算周期,清算周期就涉及到一个时间切分点,对账系统几乎都会碰到时间差问题。大部分系统都以一个自然日的起始,也就是0点到24点为清算日,但也有比较特殊的清算日规则,比如银联和银行之间的清算日是前一日的23点至当天的23点为一个清算日。

     

    以0至24点一个清算日为例,0点为切分点,本系统发起的交易,到支付通道侧,可能已经是下一个清算日,从支付渠道自身来看,本笔交易会在第二天的对账文件出现,而不是前一个清算日。

     

    如图3所示。对于这种切分点时间附近无法确认的交易,需要做一个时间窗口,时间窗口内的时间比清算日开始早一些、比清算日结束晚一些。

     

    联机交易一般有严格的时间要求,比如必须在几秒内应答完毕,大于几秒就会造成客户体验差,流失大量客户。

     

    而对账是典型的批量处理任务,几秒或者几分钟完成没有太大影响,但也不代表时间太长,几个小时就明显太长,会严重影响账务问题,无法开展日常业务。

     

    对账是典型的批量处理任务,需要批量调度平台进行调度。对于比较复杂的调度逻辑(比如依赖关系调度、灵活触发、运营界面等),需要高可用、高并发、高伸缩的分布式调度系统,可以考虑用Zeus、Quarts、Elastic-Job、Azkaban等进行定制化开发。

     

     

    下面重点介绍一下明细对账以及技术实现方案。

     

     

    (图4  明细对账总体流程)

     

    明细对账的总体流程如图4所示,包括对账文件下载、文件预处理、轧账、平帐,以及运营平台对平帐过程的监控、预警,下面进行详细介绍。

     

    对账文件下载

    对账文件一般由对账对手方根据数据库的交易记录产生,并放置和对账方约定的地方,比如文件服务器、FTP服务器等。对账方根据事先约定的方式,获取对账文件,比如通过HTTP/HTTPS下载或者FTP/SFTP拉取。

     

    对账预处理

    为了安全性、客户保密性、防篡改,会对对账文件进行加密处理,常用的有3DES、AES等对称加密或者RSA非对称加密,以及MD5、SHA1、SHA512等摘要信息,还有各种签名机制。

     

    获取到对账文件后,并不能立即进行对账,需要首先按特定安全机制进行解密,另外也需要把对账文件格式转换成内部系统方便处理的格式。这些都是轧账前的预处理。

     

    轧帐

     

     

    (图5 代收交易轧帐示意图)

     

    轧帐是会计科目流程之一,定期或者企业需要时,核对总账与明细账,每日记账是否一致。轧帐是对账最核心的流程,用来确认双方或多方的账目是否一致,不一致的情况下,有哪些差错。

     

    轧帐的正确与否,影响到后续的差错处理即平帐。以向用户代收为例,轧帐的流程如图5所示。

     

    为了确认交易记录的标准,需要双方约定,哪个或者哪些要素可以唯一确定一笔交易,比如订单号,有时也会加上时间。

     

    在具体对账的内容上,一般只需比对金额即可,某些情况下为了完备性,还会校对清算日、客户信息等其他明细数据。

     

    某些非金额因素会影响到佣金计算、多方分润等,所以也需要进行轧账确认。

     

    技术是为商业目标服务,业务发展到什么规模,就有什么样的技术与之匹配。所以,对账系统没有标准完美的技术解决方案,不同的业务场景会有不同的特性需求。

     

    尽管行业、业务规模不同导致技术方案有一定的差异性,但是差异是相对的,其中还有很多共性。下面所讨论的轧账技术方案是较通用的,当然还有很多技术细节,不展开讨论。

     

    依次读取预处理后的对账文件,根据交易标识在数据库中找出对应的记录,进行比对,检查是否一致。

     

    此种轧帐的方式非常简单,容易理解,程序实现也比较简单。缺点也显而易见,支持数据量少,如果交易量大,会导致内存不足,并且大量数据库查询,会耗尽数据库资源,对联机交易有影响。

     

    另外,因为是串行处理,在交易量大时,会大大延长轧帐时间,影响正常业务的进行。

     

    改进措施

    将对账文件按一定规则划分为小文件,确保每次只处理记录数有限的文件;

     

     按对账文件的记录数进行划分,由不同的节点进行处理,比如1-n由节点1处理,(n+1)-2n由节点2处理,(i-1)n+1-i*n由节点i处理,…充分利用分布式系统进行处理;

     

    为了减少对联机交易的影响,可以在日切之后将交易记录导入到专门的对账库或者历史库进行对账;

     

    在数据库中,新增和对账文件结构一致的对账表,将预处理后的对账文件按记录逐条导入到对账表中。利用数据库提供的join, left join,right join, case when等SQL语法进行轧帐。

     

    比如SQL:select case when a.amt<>b.amt then 1 else 0 end from a join b on a.order_no=b.order_no,可以把金额不一致的找出来。

     

     select case when a.amt is null then 1 else 0 end from a join b on a.order_no=b.order_no,可以把本地系统中不存在的交易的找出来。其他以此类推,不一一介绍。

     

    这种轧帐逻辑放在数据库,简单方便,后续方便扩展;对账对手方的数据都存储在数据库,可以非常容易可视化和排查问题。

     

    缺点是:交易量大时,导入量大,数据预热慢,数据库性能很容易成为瓶颈;所有的计算节点集中于数据库,无法利用分布式。

     

    改进措施

    批量导入数据库;

     

    根据轧帐标识进行分表分库处理,并进行汇总。

     

    Redis是高性能的key-value数据库,支持5种基本类型(String, List, Set, ZSet, Hash)以及5种基本类型的扩展。对于Set数据类型,天然的支持交集、差集、并集等集合运算。

     

    将预处理过的对账文件按轧帐标识和比对元素加载到Redis的一个Set A,本系统的交易记录也一并加载到Redis的另外一个Set B。Set A和Set B的交集即为对账对平的交易。

     

    Set A-Set B差集并不是多的记录,还需要将每个元素和Set B-Set A的差集进行查找,如果有轧帐标识一样但比对元素不一样的,即为差错交易;如果没有轧帐标识一样的元素,才能判断是多的记录。同理,可以找出少的记录。

     

    这种借助于Redis轧帐,是一种纯内存计算,预热快,速度快,充分利用Redis的成熟计算类型,没有复杂的程序逻辑处理,避免出错。但缺点是计算机内存有限,不能支持海量对账数据。

     

    改进措施

     

    按一定规则,进行数据分片,不同的数据按照轧账标识分片到不同的Redis,最后再进行汇总,但同时也增加了轧帐复杂性。

     

    如果把轧帐进行数学模型抽象的话,可以抽象为一个典型的算法问题:构建2个无重复元素的集合,按照一定比对规则找出2个集合的差集、交集,对差集和交集的元素属性进行计算比对,找出差异性。

     

    通过抽象后,我们可以看出,有很多实现方法,比如Java JDK提供的集合计算Collections、擅长搜索的Elastic Search等。

     

    这种实现方式需要视具体情况进行分析,有些实现需要花大量的时间和逻辑在数据预热上,有些实现会导致汇总数据的逻辑复杂化。

     

    总之,对账系统涉及公司的账务会计核心,尽早建立完备的对账系统,可以推动更加精细化掌握业务情况,特别是互联网金融公司。

     

    在对账系统落地时,可以综合考虑本文提出的方案,找出适合公司业务发展的技术方案,尽量使用成熟技术解决业务问题,减少技术风险性。

     

    本文的下半部分将讲述如何处理海量数据的对账,以及平帐/差错处理和虚拟账户对账,敬请期待。

     

    作者介绍

     

    王兴建

     

    现任上海秦苍(买单侠)信息科技有限公司软件架构师,加入秦苍之前,曾在中国银联、证通任职。

     

    专注于Java Core、NoSQL、分布式服务框架等技术领域,对移动支付、客户认证、红包、虚拟账户、信贷等业务领域有丰富的经验,擅长将成熟技术应用于业务。

     

    目前主要负责秦苍运营商合作业务的架构设计,以及技术在业务中的落地工作。

     

    转载于:https://www.cnblogs.com/zhimingxin/p/10685087.html

    展开全文
  • SAP笔记-abap 银行对账功能开发

    万次阅读 2009-04-14 10:01:00
    前一段时间花了几天重新设计了一下银行对账功能,其中用到了 abap OO 事件处理方法,及alv 一些用法和大家分享一下,这次修改更贴合实际业务操作. doc 档下载: http://fangkailove.download.csdn.net/银行对账...

    前一段时间花了几天重新设计了一下银行对账功能,其中用到了 abap 的 OO 事件处理方法,及alv 的一些用法和大家分享一下,这次的修改更贴合实际业务操作.

      doc 档下载:  http://fangkailove.download.csdn.net/银行对账功能-开发.doc

    银行对账功能:

       功能简价:基本功能,导入银行对账单,企业明细账和银行明细账核对,打印余额调节表.

                     增加了一些辅助功能:对于第一次对账的科目允许单边确认,对账过程中调整银行对账单等...

      基本操作流程:

    • 1. 选择公司代码和对账科目
    • 2. 新建或打开一个对账事务.
    • 3. 导入银行对账单(银行出具的科目明细账)
    • 4. 进行勾选对账,或自动逻辑对账

    开发设计:

    1.数据模型

     

     

    程序源代码(不包含屏幕的设计)

     

     

     

    *&------------------------------------------------------------------
    *& PROGRAM NAME: Z_FI_ENH_003_V2 ( old name : Z_FI_ENH_003)
    *& T-CODE:  (ZFIE003)
    *& PORGRAM TYPE: REPORT
    *& DESCRIPTION: 银行对帐功能
    *&------------------------------------------------------------------
    *& AUTHOR:  LONGXU
    *& EMAIL:   fangkailove@yeah.net
    *& DATE:    2009.03.12
    *&------------------------------------------------------------------
    *&
    *&-------------------------------------------------------------------
    *& Modification Log:
    *& Version   Date        Author       DESCRIPTION     CHANGE REQUEST
    *& -------- ----------  -----------  -------------  -----------------
    *& V1.0      2008.3.15   longxu       初始设计
    *& V1.1      2008.4.14   longxu       增加打印余调节表功能
    *& V1.2      2008.4.23   longxu       修改自动对账逻辑(  按 票号 金额 全等 且不能为空 ).
    *& V2.0      2009.3.12   longxu       重构功能并从命名 Z_FI_ENH_003_V2
    *&
    *&-------------------------------------------------------------------
    *& REFRENCE OBJECT :
    *& NAME                   TYPE                DESC
    *& ---------------------  -----------------   -----------------------
    *& ZFIE003_ACT            table               对账事务表
    *& ZFIE003_BKDOC          table               银行对账单明细账
    *& ZFIE003_EMAT           table               银行对账单已匹配
    *& ZFIE003_BMAT           table               企业明细账已匹配
    *& ZFIE003_ENMAT          table               银行对账单未匹配
    *& ZFIE003_BNMAT          table               企业明细账未匹配
    *& ZFIE003_ACTNO          号码对象             对账事务号
    *& ZFIE003_DOCNO          号码对象             银行对账单行项号
    *& ZFIE003_MATNO          号码对象             对账匹配确认号
    *& ZFIE003_S_NMAT         structure            余额调节表结构
    *& ZFIE003_SM_NMAT        smartforms           余额调节表报表
    *&
    *&-------------------------------------------------------------------

    Report  Z_FI_ENH_003_V2.

    * ALV
    TYPE-POOLS: slis.

    CONSTANTS startDate type D VALUE '20090101'.

    TABLES:ZFIE003_ENMAT,ZFIE003_BNMAT,ZFIE003_ACT,ZFIE003_BKDOC,ZFIE003_EMAT,ZFIE003_BMAT,SKB1,BSIS.


    *Select Screen
    PARAMETERS: p_BUKRS LIKE ZFIE003_ACT-BUKRS OBLIGATORY MEMORY ID buk ,"  CHAR  4 0 公司代码
                p_SAKNR LIKE ZFIE003_ACT-SAKNR OBLIGATORY.  " CHAR  10  0 总帐科目编号


    "###############################

    CLASS lcl_event_receiver DEFINITION DEFERRED.

    DATA:i_ZFIE003_ACT LIKE TABLE OF ZFIE003_ACT WITH HEADER LINE.
    DATA:wa_CurrentAct LIKE  LINE OF  i_ZFIE003_ACT.
    DATA:wa_PreAct LIKE  LINE OF  i_ZFIE003_ACT.

    DATA:i_ZFIE003_BKDOC LIKE TABLE OF ZFIE003_BKDOC WITH HEADER LINE.

    "=======================
    "Screen 150 variable
    "=======================
    DATA:FCODE_150 LIKE SY-UCOMM.

    data: grid_150_1 type  ref to cl_gui_alv_grid,
          con_150_1 type ref to   cl_gui_custom_container.
    "=============================================================

    "=======================
    "Screen 250 varialbe
    "=======================
    DATA:FCODE_250 LIKE SY-UCOMM.
    data: grid_250_1 type  ref to cl_gui_alv_grid,
          grid_250_2 type  ref to cl_gui_alv_grid,
          con_250_1 type ref to   cl_gui_custom_container.

    DATA splitter TYPE REF TO cl_gui_splitter_container.
    DATA container_1 TYPE REF TO cl_gui_container.
    DATA container_2 TYPE REF TO cl_gui_container.

    DATA: TBX_250_1 type  P  DECIMALS 2,
          TBX_250_2 TYPE   P  DECIMALS 2.
    "=============================================================




    data event_receiver type ref to lcl_event_receiver.

    DATA: BEGIN OF I_BSIS OCCURS 0 .
          INCLUDE STRUCTURE  bsis.
    DATA:   SELEC(1),
          END OF I_BSIS.



    DATA:BEGIN OF I_EMAT OCCURS 0,
          MATNO LIKE  ZFIE003_EMAT-MATNO,
          ACTNO LIKE  ZFIE003_EMAT-ACTNO,

          BUKRS LIKE  ZFIE003_EMAT-BUKRS,
          GJAHR LIKE  ZFIE003_EMAT-GJAHR,
          BELNR LIKE  ZFIE003_EMAT-BELNR,
          BUZEI LIKE  ZFIE003_EMAT-BUZEI,

          MONAT LIKE  BSIS-MONAT,
          BLDAT LIKE  BSIS-BLDAT,
          BUDAT LIKE  BSIS-BUDAT,
          SHKZG LIKE  BSIS-SHKZG,
          DMBTR LIKE  BSIS-DMBTR,
          WAERS LIKE  BSIS-WAERS,
          WRBTR LIKE  BSIS-WRBTR,
          SGTXT LIKE  BSIS-SGTXT,
          ZUONR LIKE  BSIS-ZUONR,

          SELEC LIKE  ZFIE003_BKDOC-SELEC,
         END OF I_EMAT.


    DATA:BEGIN OF I_BMAT OCCURS 0,
          MATNO LIKE  ZFIE003_BMAT-MATNO,

          ACTNO LIKE  ZFIE003_BMAT-ACTNO,
          DOCNO LIKE  ZFIE003_BMAT-DOCNO,

          BLDAT LIKE  ZFIE003_BKDOC-BLDAT,
          SHKZG LIKE  ZFIE003_BKDOC-SHKZG,
          DMBTR LIKE  ZFIE003_BKDOC-DMBTR,
          BALAC LIKE  ZFIE003_BKDOC-BALAC,
          WAERS LIKE  ZFIE003_BKDOC-WAERS,
          BKITM LIKE  ZFIE003_BKDOC-BKITM,
          PINFO LIKE  ZFIE003_BKDOC-PINFO,
          SDOCNO  LIKE  ZFIE003_BKDOC-SDOCNO,

          SELEC LIKE  ZFIE003_BKDOC-SELEC,

         END OF I_BMAT.





    "$ Region Class Definition and implementation

    ****************************************************************
    * LOCAL CLASSES: Definition
    ****************************************************************
    *===============================================================
    * class lcl_event_receiver: local class to
    *                         define and handle own functions.
    *
    * Definition:
    * ~~~~~~~~~~~
    CLASS lcl_event_receiver DEFINITION.

      PUBLIC SECTION.

        CLASS-METHODS  grid_250_1_toolbar     FOR EVENT toolbar OF cl_gui_alv_grid
            IMPORTING e_object e_interactive.

        CLASS-METHODS  grid_250_1_user_command  FOR EVENT user_command OF cl_gui_alv_grid
                  IMPORTING e_ucomm.


        CLASS-METHODS  grid_250_2_toolbar  FOR EVENT toolbar OF cl_gui_alv_grid
                IMPORTING e_object e_interactive.

        CLASS-METHODS  grid_250_2_user_command  FOR EVENT user_command OF cl_gui_alv_grid
                  IMPORTING e_ucomm.





        CLASS-METHODS  handle_menu_button   FOR EVENT menu_button OF cl_gui_alv_grid
                IMPORTING e_object e_ucomm.

        CLASS-METHODS  handle_user_after_user_command   FOR EVENT after_user_command OF cl_gui_alv_grid.

        CLASS-METHODS  handle_user_befor_user_command   FOR EVENT before_user_command OF cl_gui_alv_grid.

        CLASS-METHODS  Handle_data_changed  FOR EVENT data_changed OF cl_gui_alv_grid
             IMPORTING er_data_changed.



      PRIVATE SECTION.

    ENDCLASS.
    *
    * lcl_event_receiver (Definition)
    *===============================================================

    ****************************************************************
    * LOCAL CLASSES: Implementation
    ****************************************************************
    *===============================================================
    * class lcl_event_receiver (Implementation)
    *
    *
    CLASS lcl_event_receiver IMPLEMENTATION.


    METHOD grid_250_1_toolbar.
        data:gs_toolbar  TYPE stb_button.
        data:icount type i.

        IF wa_CurrentAct-EDDAT <> '00000000'.
              "MESSAGE '当前事务已经被关账过,不可再修改!' type 'E'.
              EXIT.
        ENDIF.

        DESCRIBE TABLE i_ZFIE003_ACT LINES icount.
        IF icount > 1.
          "MESSAGE '当前不是第一次对账,不能无条件单边确认' type 'E'.
          EXIT.
        ENDIF.


        CLEAR gs_toolbar.
        MOVE 'IniMat' TO gs_toolbar-function.

        MOVE ICON_TRANSPORT TO gs_toolbar-icon.
        MOVE '无条件企业单边确认'(200) TO gs_toolbar-quickinfo.

        MOVE space TO gs_toolbar-disabled.
        APPEND gs_toolbar TO e_object->mt_toolbar.



      ENDMETHOD.

     METHOD grid_250_1_user_command.
       CASE e_ucomm.
          WHEN 'IniMat'.

          data: f_MATNO like ZFIE003_EMAT-MATNO.
          data: wa_BSIS LIKE LINE OF I_BSIS.

          CALL  FUNCTION 'NUMBER_GET_NEXT'
            EXPORTING
              NR_RANGE_NR         = '01'
              OBJECT          = 'ZSN_MATNO'
              QUANTITY          = '1'
            IMPORTING
              NUMBER          = f_MATNO
            EXCEPTIONS
              INTERVAL_NOT_FOUND    = 1
              NUMBER_RANGE_NOT_INTERN = 2
              OBJECT_NOT_FOUND      = 3
              QUANTITY_IS_0       = 4
              QUANTITY_IS_NOT_1     = 5
              INTERVAL_OVERFLOW     = 6
              BUFFER_OVERFLOW       = 7
              OTHERS          = 8.
          IF SY-SUBRC NE 0.
             MESSAGE '取对账成功确认号错误!' type 'E'.
            EXIT.
          ENDIF.

          LOOP AT I_BSIS INTO wa_BSIS WHERE SELEC = 'X'.
             ZFIE003_EMAT-MATNO = f_MATNO.
             ZFIE003_EMAT-ACTNO = wa_CurrentAct-ACTNO.
             ZFIE003_EMAT-BUKRS = wa_BSIS-BUKRS.
             ZFIE003_EMAT-GJAHR = wa_BSIS-GJAHR.
             ZFIE003_EMAT-BELNR = wa_BSIS-BELNR.
             ZFIE003_EMAT-BUZEI = wa_BSIS-BUZEI.
             INSERT INTO ZFIE003_EMAT VALUES ZFIE003_EMAT .
          ENDLOOP.


          IF sy-subrc <> 0.
             ROLLBACK WORK.
             exit.
           else.
             COMMIT WORK.
             PERFORM GetBsisFromDb.
             clear tbx_250_1.
             CALL METHOD grid_250_1->refresh_table_display.
          ENDIF.

        ENDCASE.
      ENDMETHOD.



      METHOD grid_250_2_toolbar.
    ** § 2.At event TOOLBAR define a toolbar element of type 1 by using
    **     event paramenter E_OBJECT. Remember its function code.
    **.......
    ** Part I: Define a menu button including a function code that
    **         is evaluated in 'handle_MENU_BUTTON
    **.......
    *
    *
    ** append a separator to normal toolbar
    *    CLEAR gs_toolbar.
    *    MOVE 3 TO gs_toolbar-butn_type.
    *    APPEND gs_toolbar TO e_object->mt_toolbar.
    *
    **...................
    ** append a menu with default button (Type 1)
    ** The function code of the default button is the same as
    ** the one for the menu.
    ** If the user klicks on the default button ALV raises
    ** directly event BEFORE_USER_COMMAND
    ** (then USER_COMMAND, AFTER_USER_COMMAND).
    ** If the user klicks on the menu button ALV raises event MENU_BUTTON.
    *

        data:gs_toolbar  TYPE stb_button.

        IF wa_CurrentAct-EDDAT <> '00000000'.
              "MESSAGE '当前事务已经被关账过,不可再修改!' type 'E'.
              EXIT.
        ENDIF.

        CLEAR gs_toolbar.
        MOVE 'DELETE' TO gs_toolbar-function.
    * --> This function code is evaluated in 'handle_menu_button'
        MOVE ICON_DELETE_ROW TO gs_toolbar-icon.
        MOVE '删除选取中行'(200) TO gs_toolbar-quickinfo.
        "MOVE 1 TO gs_toolbar-butn_type.
        MOVE space TO gs_toolbar-disabled.
        APPEND gs_toolbar TO e_object->mt_toolbar.
      ENDMETHOD.

     METHOD grid_250_2_user_command.
       CASE e_ucomm.
          WHEN 'DELETE'.

            data: wa_ZFIE003_BKDOC like LINE OF i_ZFIE003_BKDOC.

            LOOP AT i_ZFIE003_BKDOC INTO wa_ZFIE003_BKDOC.
              IF wa_ZFIE003_BKDOC-selec = 'X'.
                 DELETE FROM ZFIE003_BKDOC where docno = wa_ZFIE003_BKDOC-DocNO.
                 "delete TABLE i_ZFIE003_BKDOC FROM wa_ZFIE003_BKDOC.
              ENDIF.
            ENDLOOP.

            PERFORM GetBKDocFromDb.
            clear tbx_250_2.
            CALL METHOD grid_250_2->refresh_table_display.

        ENDCASE.
      ENDMETHOD.                           "grid_250_2_user_command


    *--------------------------------------------------------------------
      METHOD handle_user_after_user_command .
          "BREAK-POINT.
      ENDMETHOD.

      METHOD handle_user_befor_user_command .
          "BREAK-POINT.
      ENDMETHOD.

      METHOD handle_menu_button.
    ** § 3.At event MENU_BUTTON query your function code and define a
    **     menu in the same way as a context menu.
    **..........
    ** Part II: Evaluate 'e_ucomm' to see which menu button of the toolbar
    **          has been clicked on.
    **          Define then the corresponding menu.
    **          The menu contains function codes that are evaluated
    **          in 'grid_250_2_user_command'.
    **...........
    *
    **  query e_ucomm to find out which menu button has been clicked on
    *    IF e_ucomm = 'TO_SFLIGHT'.
    *      CALL METHOD e_object->add_function
    *                  EXPORTING fcode   = 'TO_SPFLI'
    *                            text    = text-100. "Overview
    **  § 3a.) choose a default function and define the same function code
    **         as used for the menu.
    *      CALL METHOD e_object->add_function
    *                  EXPORTING fcode   = 'TO_SFLIGHT'
    *                            text    = text-200. "Flights
    *
    *    ENDIF.
      ENDMETHOD.


     METHOD  handle_data_changed.
      "BREAK-POINT.
     ENDMETHOD.

    *---------------------------------------------------------------------









    ENDCLASS.
    *
    * lcl_event_receiver (Implementation)
    *===================================================================
    "$ Endregion Class Definition and implementation






    "$ Region Main proc
    START-OF-SELECTION.
     IF sy-UNAME = 'HS014'.
       data: clear(5).
       "重新初始化对账系统可以进入调试模式 将 clear 值设为 'HS014'.
       BREAK-POINT.
       IF clear = 'HS014'.
         PERFORM ClearAllUserTableData.
       ENDIF.
     ENDIF.
     SELECT SINGLE * FROM SKB1 WHERE BUKRS =  p_BUKRS AND SAKNR = p_SAKNR .

     IF sy-subrc <> 0.
        MESSAGE '科目不存在!' type 'I'.
     else.
       call screen '0150'.
     ENDIF.
    "$ Endregion Main proc




    "###############################
    FORM ClearAllUserTableData.
    *& ZFIE003_ACT            table               对账事务表
      delete from ZFIE003_ACT.
    *& ZFIE003_BKDOC          table               银行对账单明细账
      delete from ZFIE003_BKDOC.
    *& ZFIE003_EMAT           table               银行对账单已匹配
      delete from ZFIE003_EMAT.
    *& ZFIE003_BMAT           table               企业明细账已匹配
      delete from ZFIE003_BMAT.
    *& ZFIE003_ENMAT          table               银行对账单未匹配
      delete from ZFIE003_ENMAT.
    *& ZFIE003_BNMAT          table               企业明细账未匹配
      delete from ZFIE003_BNMAT.
    ENDFORM.




    "###############################
    "$ region screen 150
    *********************************************

    FORM S150_ALV_INI.

    data: gt_fieldcat  type lvc_t_fcat.
    data: gs_layout type lvc_s_layo.
    data: gs_variant type disvariant.



      if con_150_1 is initial.
       create object con_150_1
       exporting container_name = 'CON_150_1'.
       create object grid_150_1
       exporting
       i_parent = con_150_1.
      endif.



      "定义grid格式
      clear gs_layout.
      gs_layout-smalltitle  = ''.
      gs_layout-grid_title  = ''.
      gs_layout-detailtitl  = '细节信息'(003).
      gs_layout-zebra   =  'X'."定义GRID的样式如斑马条式
      gs_layout-sel_mode  = 'B'.
      gs_layout-cwidth_opt  = 'X'.
      gs_layout-no_toolbar  = ''.
      "gs_layout-BOX_FNAME = 'MARK'.

      "定义列
      DATA: ls_fieldcat like line of gt_fieldcat.
      CLEAR gt_fieldcat[].

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'ACTNO'.
      ls_fieldcat-coltext = 'ID'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BUKRS'.
      ls_fieldcat-coltext = '公司代码'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SAKNR'.
      ls_fieldcat-coltext = '总帐科目编号'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'STEXT'.
      ls_fieldcat-coltext = '科目描述'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BEDAT'.
      ls_fieldcat-coltext = '开启事务时间'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'EDDAT'.
      ls_fieldcat-coltext = '结束事务时间'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BUDAT'.
      ls_fieldcat-coltext = '对账截止日期'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'ETBLC'.
      ls_fieldcat-coltext = '企业余额'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BKBLC'.
      ls_fieldcat-coltext = '银行余额'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'WAERS'.
      ls_fieldcat-coltext = '科目币别'.
      APPEND ls_fieldcat TO gt_fieldcat.

      gs_variant-report = sy-repid. "指定保存变式的程序名.
      call  method grid_150_1->set_table_for_first_display
         exporting
          is_variant    = gs_variant
          is_layout      =  gs_layout"采用自定义的格式
          I_SAVE = 'A'
          I_DEFAULT = 'X'

         changing
          it_outtab      =  i_ZFIE003_ACT[]
          it_fieldcatalog  = gt_fieldcat[].

    ENDFORM.





    FORM UpdateCurrentActToDB.

      UPDATE ZFIE003_ACT
        SET BUDAT = wa_CurrentAct-BUDAT
            EDDAT = wa_CurrentAct-EDDAT
            ETBLC = wa_CurrentAct-ETBLC
            BKBLC = wa_CurrentAct-BKBLC
      WHERE ACTNO = wa_CurrentAct-ACTNO.

    ENDFORM.

    FORM GetActorFromDB.
      SELECT * INTO TABLE I_ZFIE003_ACT FROM ZFIE003_ACT WHERE BUKRS = p_BUKRS AND SAKNR = p_SAKNR.
    ENDFORM.

    FORM CreateActor.

      "DATA: FACTNO LIKE I_ZFIE003_ACT-ACTNO.

      SELECT SINGLE *  FROM ZFIE003_ACT WHERE BUKRS = p_BUKRS AND SAKNR = p_SAKNR AND EDDAT = '00000000'.
      IF sy-subrc = 0.
        MESSAGE '科目已开启了一个对账事务' type 'E'.
        exit.
      ENDIF.

      data: l_answer.
        CALL FUNCTION 'POPUP_TO_CONFIRM_WITH_MESSAGE'
          EXPORTING
            defaultoption = 'N'
            diagnosetext1 = '开启一个新的对账事物,'
            textline1     = '确定继续?'
            titel         = '询问'
          IMPORTING
            answer        = l_answer.

        IF l_answer <> 'J'.
          EXIT.
        ENDIF.

      CLEAR wa_CurrentAct.

      CALL  FUNCTION 'NUMBER_GET_NEXT'
       EXPORTING
         NR_RANGE_NR         = '01'
         OBJECT          = 'ZSN_ACTNO'
         QUANTITY          = '1'
       IMPORTING
         NUMBER          = wa_CurrentAct-ACTNO
       EXCEPTIONS
         INTERVAL_NOT_FOUND    = 1
         NUMBER_RANGE_NOT_INTERN = 2
         OBJECT_NOT_FOUND      = 3
         QUANTITY_IS_0       = 4
         QUANTITY_IS_NOT_1     = 5
         INTERVAL_OVERFLOW     = 6
         BUFFER_OVERFLOW       = 7
         OTHERS          = 8.
      IF SY-SUBRC NE 0.
        MESSAGE '取对账事务号错误!' type 'E'.
       EXIT.
      ENDIF.

      wa_CurrentAct-BUKRS = p_BUKRS.
      wa_CurrentAct-SAKNR = p_SAKNR.
      wa_CurrentAct-BUDAT = sy-datum.
      wa_CurrentAct-BEDAT = sy-datum.
      wa_CurrentAct-WAERS = SKB1-WAERS.

      select SINGLE TXT50 INTO wa_CurrentAct-STEXT from skat inner join t001 on t001~BUKRS = p_BUKRS and t001~KTOPL = skat~KTOPL where SAKNR = p_SAKNR AND skat~SPRAS = '1'.

      SELECT SINGLE * FROM SKB1 WHERE BUKRS =  p_BUKRS AND SAKNR = p_SAKNR .

      PERFORM GetAccountBalance USING wa_CurrentAct-bukrs
                                      wa_CurrentAct-SAKNR
                                      wa_CurrentAct-BUDAT
                                      wa_CurrentAct-WAERS
                             CHANGING wa_CurrentAct-ETBLC.

      MOVE wa_CurrentAct to ZFIE003_ACT.
      INSERT  ZFIE003_ACT.


      IF SY-SUBRC NE 0.
        ROLLBACK work.
      else.
        commit work.
      ENDIF.



      "todo 将上期未匹配数据 copy 到本次银行对账明细表中....
      DATA: preActNo like ZFIE003_ACT-ACTNO.
      data: lc type i.
      preActNo = 0.
      DESCRIBE TABLE i_ZFIE003_ACT lines lc.
      IF lc > 0.
        LOOP AT i_ZFIE003_ACT.
          IF preActNo < i_ZFIE003_ACT-ACTNO .
            preActNo = i_ZFIE003_ACT-ACTNO .
          ENDIF.
        ENDLOOP.
      ENDIF.

      CLEAR I_ZFIE003_BKDOC[].
      IF preActNo > 0.
        SELECT
          *
        INTO CORRESPONDING FIELDS OF TABLE I_ZFIE003_BKDOC
        FROM ZFIE003_BKDOC
        INNER JOIN ZFIE003_BNMAT ON ZFIE003_BKDOC~DOCNO = ZFIE003_BNMAT~DOCNO
        WHERE ZFIE003_BKDOC~ACTNO = preActNo.

        IF sy-subrc = 0.
          LOOP AT I_ZFIE003_BKDOC.
            I_ZFIE003_BKDOC-ACTNO = wa_CurrentAct-ACTNO.
             "取序号码
            CALL  FUNCTION 'NUMBER_GET_NEXT'
             EXPORTING
               NR_RANGE_NR         = '01'
               OBJECT          = 'ZSN_DOCNO'
               QUANTITY          = '1'
             IMPORTING
               NUMBER          = I_ZFIE003_BKDOC-DOCNO
             EXCEPTIONS
               INTERVAL_NOT_FOUND    = 1
               NUMBER_RANGE_NOT_INTERN = 2
               OBJECT_NOT_FOUND      = 3
               QUANTITY_IS_0       = 4
               QUANTITY_IS_NOT_1     = 5
               INTERVAL_OVERFLOW     = 6
               BUFFER_OVERFLOW       = 7
               OTHERS          = 8.
            IF SY-SUBRC NE 0.
              MESSAGE '取银行对账行号码错误!' type 'I'.
             EXIT.
            ENDIF.
            INSERT INTO ZFIE003_BKDOC VALUES I_ZFIE003_BKDOC.
          ENDLOOP.
        ENDIF.
      ENDIF.

      IF SY-SUBRC NE 0.
        ROLLBACK work.
      else.
        commit work.
      ENDIF.

    ENDFORM.



    FORM OpenActor.
      DATA:gi_index_rows TYPE lvc_t_row WITH HEADER LINE.
      DATA:l_lines type i.

      CALL METHOD grid_150_1->get_selected_rows
        IMPORTING
          et_index_rows = gi_index_rows[].
      DESCRIBE TABLE gi_index_rows LINES l_lines.
      IF l_lines = 0.
        MESSAGE '请选中一个对账事务!' type 'E'.
        EXIT.
      ENDIF.

      CLEAR wa_CurrentAct .
      READ TABLE gi_index_rows INDEX 1.
      READ TABLE I_ZFIE003_ACT INDEX  gi_index_rows-index into wa_CurrentAct .

      PERFORM GetBKDocFromDb.

      PERFORM GetBsisFromDb.

      IF wa_CurrentAct-EDDAT = '00000000'. "没结账前更新余额.
        PERFORM GetAccountBalance USING wa_CurrentAct-bukrs
                                        wa_CurrentAct-SAKNR
                                        wa_CurrentAct-BUDAT
                                        wa_CurrentAct-WAERS
                               CHANGING wa_CurrentAct-ETBLC.
        PERFORM UpdateCurrentActToDB.
      ENDIF.


    ENDFORM.



    FORM GetAccountBalance USING bukrs like i_ZFIE003_ACT-BUKRS
                                 saknr like i_ZFIE003_ACT-SAKNR
                                 budat like i_ZFIE003_ACT-BUDAT
                                 WAERS like i_ZFIE003_ACT-WAERS
                           CHANGING rlt like i_ZFIE003_ACT-ETBLC.
      DATA: i_ACCOUNT_BALANCES  LIKE  BAPI3006_4 OCCURS 0.
      DATA: i_line LIKE LINE OF i_ACCOUNT_BALANCES.
      DATA: i_bsis like bsis OCCURS 0 WITH HEADER LINE.
      DATA: CURR LIKE BAPI3006_5-CURR_TYPE.
      DATA: count type i.
    *
    *
    *  CALL FUNCTION 'BAPI_GL_ACC_GETPERIODBALANCES'
    *     EXPORTING
    *
    *      COMPANYCODE   =  bukrs
    *      GLACCT        =  saknr
    *      FISCALYEAR    =  budat(4)
    *      CURRENCYTYPE  =  '00'
    *     TABLES
    *      ACCOUNT_BALANCES = i_ACCOUNT_BALANCES.
    *
    *     DESCRIBE TABLE i_ACCOUNT_BALANCES LINES  count.
    *
    *     IF count = 0.
    *        CALL FUNCTION 'BAPI_GL_ACC_GETPERIODBALANCES'
    *           EXPORTING
    *
    *            COMPANYCODE   =  bukrs
    *            GLACCT        =  saknr
    *            FISCALYEAR    =  budat(4)
    *            CURRENCYTYPE  =  '10'
    *           TABLES
    *            ACCOUNT_BALANCES = i_ACCOUNT_BALANCES.
    *     ENDIF.
    *
    *
    *     READ TABLE i_ACCOUNT_BALANCES INDEX  budat+4(2) INTO  i_line   .
    *
    *     IF sy-subrc <> 0.
    *       exit.
    *     ENDIF.
    *
    *     rlt = i_line-BALANCE.

         DATA: SPSWBT like bsis-PSWBT,
               HPSWBT LIKE bsis-PSWBT.

         IF WAERS = 'RMB'.
           select sum( dmbtr ) into SPSWBT  FROM bsis where bsis~bukrs = bukrs and bsis~HKONT = SAKNR and bsis~budat <= budat and bsis~SHKZG = 'S'.
           select sum( dmbtr ) into HPSWBT  FROM bsis where bsis~bukrs = bukrs and bsis~HKONT = SAKNR and bsis~budat <= budat and bsis~SHKZG = 'H'.
         ELSE.
           select sum( wrbtr ) into SPSWBT  FROM bsis where bsis~bukrs = bukrs and bsis~HKONT = SAKNR and bsis~budat <= budat and bsis~SHKZG = 'S'.
           select sum( wrbtr ) into HPSWBT  FROM bsis where bsis~bukrs = bukrs and bsis~HKONT = SAKNR and bsis~budat <= budat and bsis~SHKZG = 'H'.
         ENDIF.

    *     LOOP AT i_bsis.
    *       IF i_bsis-SHKZG = 'S'.
    *         rlt = rlt - i_bsis-PSWBT .
    *       ELSE.
    *         rlt = rlt + i_bsis-PSWBT .
    *       ENDIF.
    *     ENDLOOP.

          rlt =  SPSWBT - HPSWBT .

    ENDFORM.




    DATA: f_bsis like bsis."  OCCURS 0 WITH HEADER LINE.
    FORM LOOP_OUTPUT.

      MOVE-CORRESPONDING f_bsis to I_BSIS.
      IF I_BSIS-SHKZG = 'H'.
            I_BSIS-dmbtr = - I_BSIS-dmbtr.
            I_BSIS-wrbtr = - I_BSIS-wrbtr.
            modify table I_BSIS.
      ENDIF.
      APPEND I_BSIS.
    ENDFORM.
    FORM GetBsisFromDb.
      CLEAR I_BSIS[].

      IF wa_CurrentAct-EDDAT = '00000000'. "没结账前从bsis取数
        "对当前表检索出不存于另一表的数据 , 用open sql 效率较低,采用 native sql (oracle)
        EXEC SQL PERFORMING LOOP_OUTPUT.
          SELECT *
          INTO  :f_bsis
          FROM BSIS
          WHERE bsis.bukrs = :wa_CurrentAct-bukrs
            and bsis.HKONT = :wa_CurrentAct-SAKNR
            and bsis.budat <= :wa_CurrentAct-budat
            and bsis.budat >= :startDate
            and bsis.MANDT = :SY-MANDT
            and NOT EXISTS (select *
                            from ZFIE003_EMAT
                            where ZFIE003_EMAT.BUKRS = bsis.bukrs
                              and ZFIE003_EMAT.GJAHR = bsis.GJAHR
                              and ZFIE003_EMAT.BELNR = bsis.BELNR
                              and ZFIE003_EMAT.BUZEI = bsis.BUZEI
                              and ZFIE003_EMAT.MANDT = :SY-MANDT )
        ENDEXEC.
      ELSE.   关账后只取未核对数据表 zfie003_enmat
        "BREAK-POINT.
        EXEC SQL PERFORMING LOOP_OUTPUT.
          SELECT
            bsis.*
          INTO  :f_bsis
          FROM BSIS
          INNER JOIN ZFIE003_ENMAT ON ZFIE003_ENMAT.BUKRS = bsis.bukrs
                                  and ZFIE003_ENMAT.GJAHR = bsis.GJAHR
                                  and ZFIE003_ENMAT.BELNR = bsis.BELNR
                                  and ZFIE003_ENMAT.BUZEI = bsis.BUZEI
                                  and ZFIE003_ENMAT.MANDT = bsis.MANDT
                                  and ZFIE003_ENMAT.ACTNO = :wa_CurrentAct-ActNO
                                  and bsis.MANDT = :SY-MANDT

        ENDEXEC.

      ENDIF.
    ENDFORM.



    "DATA: f_bsis like bsis."  OCCURS 0 WITH HEADER LINE.
    FORM LOOP_BKDOC.
      APPEND I_ZFIE003_BKDOC.
    ENDFORM.
    FORM GetBKDocFromDb.
      CLEAR I_ZFIE003_BKDOC[].
      "对当前表检索出不存于另一表的数据 , 用open sql 效率较低,采用 native sql (oracle)
      EXEC SQL PERFORMING LOOP_BKDOC.
        SELECT *
        INTO :I_ZFIE003_BKDOC
        FROM ZFIE003_BKDOC
        WHERE ZFIE003_BKDOC.ACTNO = :wa_CurrentAct-ACTNO
          and ZFIE003_BKDOC.MANDT = :SY-MANDT
          and NOT EXISTS (select *
                          from ZFIE003_BMAT
                          where ZFIE003_BMAT.ACTNO = :wa_CurrentAct-ACTNO
                            and ZFIE003_BMAT.DOCNO = ZFIE003_BKDOC.DOCNO
                            and ZFIE003_BMAT.MANDT = :SY-MANDT )
      ENDEXEC.

    ENDFORM.



    "PROCESS BEFORE OUTPUT.
     MODULE STATUS_0150 OUTPUT.
       set pf-status 'G150'.
       set TITLEBAR  'T150'.

       PERFORM GetActorFromDB .

       PERFORM S150_ALV_INI.



     ENDMODULE.






    "PROCESS AFTER INPUT.
     MODULE USER_COMMAND_0150 INPUT.
      " BREAK-POINT.
      case  FCODE_150.
       when 'BACK'.
         Clear FCODE_150.
         LEAVE to screen 0 .
       when 'EXIT'.
         Clear FCODE_150.
         LEAVE PROGRAM.
       when 'CANCEL'.
         Clear FCODE_150.
         leave to screen 0 .
       WHEN 'NEW'.
         Clear FCODE_150.



         PERFORM CreateActor .
         PERFORM GetActorFromDB .
         CALL METHOD grid_150_1->refresh_table_display.


       When 'OPEN'.
         "BREAK-POINT.
         Clear FCODE_150.
         PERFORM OpenActor.

         call SCREEN '0250'.
         CALL METHOD grid_150_1->refresh_table_display.

      endcase.

     ENDMODULE.





    "$ endregion  screen 150.
    "###############################

    "###############################
    "$ Region screen 250

    FORM RefreshSelectAmont.
        CALL METHOD grid_250_1->check_changed_data.
        CALL METHOD grid_250_2->check_changed_data.

        TBX_250_1 = 0.
        DATA: wa_BSIS LIKE LINE OF I_BSIS,
              wa_ZFIE003_BKDOC LIKE LINE OF I_ZFIE003_BKDOC.

        IF wa_CurrentAct-WAERS = 'RMB'.
          LOOP AT I_BSIS INTO wa_BSIS WHERE SELEC = 'X'.
            TBX_250_1 = TBX_250_1 + wa_BSIS-dmbtr.
          ENDLOOP.
        ELSE.
          LOOP AT I_BSIS INTO wa_BSIS WHERE SELEC = 'X'.
            TBX_250_1 = TBX_250_1 + wa_BSIS-wrbtr.
          ENDLOOP.
        ENDIF.

        TBX_250_2 = 0.
        LOOP AT  I_ZFIE003_BKDOC INTO wa_ZFIE003_BKDOC WHERE SELEC = 'X'.
           TBX_250_2 = TBX_250_2 + wa_ZFIE003_BKDOC-DMBTR.
        ENDLOOP.
    ENDFORM.

    "检查余额调节表是否平衡。
    FORM CheckBalance CHANGING IsOK TYPE C.
        DATA: wa_BSIS LIKE LINE OF I_BSIS,
              wa_ZFIE003_BKDOC LIKE LINE OF I_ZFIE003_BKDOC.

        DATA: b1 type  P  DECIMALS 2, "企业余额
          b2 TYPE   P  DECIMALS 2.    "银行余额

        IF wa_CurrentAct-WAERS = 'RMB'.
          LOOP AT I_BSIS INTO wa_BSIS .
            b1 = b1 + wa_BSIS-dmbtr.
          ENDLOOP.
        ELSE.
          LOOP AT I_BSIS INTO wa_BSIS .
            b1 = b1 + wa_BSIS-wrbtr.
          ENDLOOP.
        ENDIF.

        b2 = 0.
        LOOP AT  I_ZFIE003_BKDOC INTO wa_ZFIE003_BKDOC .
           b2 = b2 + wa_ZFIE003_BKDOC-DMBTR.
        ENDLOOP.


        b1 = b1 + wa_CurrentAct-BKBLC. " 余业余额+企业未达
        b2 = b2 + wa_CurrentAct-ETBLC. " 银行余额_银行未达

        IsOK = ''.
        IF b1 = b2.
          IsOK = 'X'.
        ENDIF.
    ENDFORM.





    FORM S250_ALV_INI.

    data: gt_fieldcat  type lvc_t_fcat.
    data: gs_layout type lvc_s_layo.
    data: gs_variant type disvariant.
    data:gt_toolbar_excluding   Type  UI_FUNCTIONS.



      if con_250_1 is initial.
       create object con_250_1
       exporting container_name = 'CON_250_1'.

       CREATE OBJECT splitter
                      EXPORTING parent = con_250_1
                                rows    = 1
                                ALIGN = 15
                                columns = 2.

       CALL METHOD splitter->get_container
                          EXPORTING row      = 1
                                    column   = 1
                          RECEIVING container = container_1.

       CALL METHOD splitter->get_container
                      EXPORTING row      = 1
                                column   = 2
                      RECEIVING container = container_2.


       create object grid_250_1
       exporting
       i_parent = container_1.

       create object grid_250_2
       exporting
       i_parent = container_2.





      "$ "$ Region grid1

      "定义 gird toolbar
      CLEAR gt_toolbar_excluding.
      APPEND:
              cl_gui_alv_grid=>MC_FC_PRINT               to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_DRAG_DROP_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_NO_DELETE_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_NO_INSERT_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_TO_OFFICE to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_ABC    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CHAIN    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CRBATCH    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CRWEB    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_LINEITEMS    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_MASTER_DATA    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_MORE    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_REPORT    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_XINT    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_XXL    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CHECK    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_MB_EXPORT    to gt_toolbar_excluding,

              cl_gui_alv_grid=>MC_FC_GRAPH     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_HELP     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_HTML     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_INFO     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_PC_FILE   to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_VIEWS   to gt_toolbar_excluding,

              cl_gui_alv_grid=>MC_FC_LOC_APPEND_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_COPY     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_COPY_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_CUT     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_DELETE_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_INSERT_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_MOVE_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_PASTE     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_PASTE_NEW_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_REFRESH    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_UNDO     to gt_toolbar_excluding
              .


     "定义grid格式
      clear gs_layout.
      gs_layout-smalltitle  = ''.
      gs_layout-grid_title  = ''.
      gs_layout-zebra   =  'X'."定义GRID的样式如斑马条式
      gs_layout-sel_mode  = 'B'.
      gs_layout-no_toolbar  = ''.
      gs_layout-NO_ROWMARK = 'X'.
      gs_layout-EDIT_MODE = ''.

      "定义列
      DATA: ls_fieldcat like line of gt_fieldcat.
      CLEAR gt_fieldcat[].

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SELEC'.
      ls_fieldcat-coltext = '选择'.
      ls_fieldcat-outputlen = 2.
      ls_fieldcat-checkbox = 'X'.
      "ls_fieldcat-input = 'X'.
      ls_fieldcat-edit = 'X'.

      APPEND ls_fieldcat TO gt_fieldcat.



      CONCATENATE sy-repid 'left' into   gs_variant-report  . "指定保存变式的程序名.
      call  method grid_250_1->set_table_for_first_display
         exporting
          is_variant    = gs_variant
          is_layout      =  gs_layout"采用自定义的格式
          it_toolbar_excluding = gt_toolbar_excluding
          i_structure_name = 'BSIS'
          I_SAVE = 'A'
          I_DEFAULT = 'X'

         changing
          it_outtab      =  i_bsis[]
          it_fieldcatalog  = gt_fieldcat[].
          .

     "$ Endregion grid1




      "$ "$ Region grid2

      "定义 gird toolbar
      CLEAR gt_toolbar_excluding.
      APPEND:
              cl_gui_alv_grid=>MC_FC_PRINT               to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_DRAG_DROP_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_NO_DELETE_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_NO_INSERT_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_TO_OFFICE to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_ABC    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CHAIN    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CRBATCH    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CRWEB    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_LINEITEMS    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_MASTER_DATA    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_MORE    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_REPORT    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_XINT    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_XXL    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CHECK    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_MB_EXPORT    to gt_toolbar_excluding,

              cl_gui_alv_grid=>MC_FC_GRAPH     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_HELP     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_HTML     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_INFO     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_PC_FILE   to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_VIEWS   to gt_toolbar_excluding,

              cl_gui_alv_grid=>MC_FC_LOC_APPEND_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_COPY     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_COPY_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_CUT     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_DELETE_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_INSERT_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_MOVE_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_PASTE     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_PASTE_NEW_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_REFRESH    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_UNDO     to gt_toolbar_excluding
              .



      "定义grid格式
      clear gs_layout.
      gs_layout-smalltitle  = ''.
      gs_layout-grid_title  = ''.
      gs_layout-zebra   =  'X'."定义GRID的样式如斑马条式
      gs_layout-sel_mode  = 'B'.
      gs_layout-no_toolbar  = ''.
      gs_layout-NO_ROWMARK = 'X'.
      gs_layout-EDIT_MODE = ''.


      "定义列


      CLEAR gt_fieldcat[].

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SELEC'.
      ls_fieldcat-coltext = '选择'.
      ls_fieldcat-outputlen = 2.
      ls_fieldcat-checkbox = 'X'.
      "ls_fieldcat-input = 'X'.
      ls_fieldcat-edit = 'X'.

      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'DOCNO'.
      ls_fieldcat-coltext = '行号'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'ACTNO'.
      ls_fieldcat-coltext = '事务ID'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BLDAT'.
      ls_fieldcat-coltext = '日期'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SHKZG'.
      ls_fieldcat-coltext = '借方/贷方标识'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'DMBTR'.
      ls_fieldcat-coltext = '发生金额'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BALAC'.
      ls_fieldcat-coltext = '余额'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'WAERS'.
      ls_fieldcat-coltext = '货币'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BKITM'.
      ls_fieldcat-coltext = '传票号'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'PINFO'.
      ls_fieldcat-coltext = '对方信息'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SDOCNO'.
      ls_fieldcat-coltext = '复制行号'.
      APPEND ls_fieldcat TO gt_fieldcat.






      CONCATENATE sy-repid 'right' into   gs_variant-report  . "指定保存变式的程序名.
      call  method grid_250_2->set_table_for_first_display
         exporting
          is_variant    = gs_variant
          is_layout      =  gs_layout"采用自定义的格式
          it_toolbar_excluding = gt_toolbar_excluding

          I_SAVE = 'A'
          I_DEFAULT = 'X'


         changing
          it_outtab      =  i_ZFIE003_BKDOC[]
          it_fieldcatalog  = gt_fieldcat[].




      SET HANDLER event_receiver->grid_250_1_user_command
                  event_receiver->grid_250_1_toolbar FOR  grid_250_1 .
      CALL METHOD grid_250_1->set_toolbar_interactive.

      SET HANDLER event_receiver->grid_250_2_user_command
                  event_receiver->grid_250_2_toolbar FOR  grid_250_2 .
      CALL METHOD grid_250_2->set_toolbar_interactive.


     "$ Endregion grid2
      endif.

      IF sy-ucomm = 'OPEN'.
            clear tbx_250_1.
            clear tbx_250_2.
            CALL METHOD grid_250_1->refresh_table_display.
            CALL METHOD grid_250_2->refresh_table_display.
    *    else.
    *
    *    DATA: l_valid(1) TYPE c.
    *
    *    CALL METHOD grid_250_1->check_changed_data
    *      CHANGING
    *            c_refresh = l_valid.
    *    IF l_valid = 'X'.
    *      CALL METHOD grid_250_1->refresh_table_display.
    *    ENDIF.
    *
    *    CALL METHOD grid_250_2->check_changed_data
    *      CHANGING
    *            c_refresh = l_valid.
    *    IF l_valid = 'X'.
    *      CALL METHOD grid_250_2->refresh_table_display.
    *    ENDIF.

        clear sy-ucomm.
      ENDIF.
    ENDFORM.




    *PROCESS BEFORE OUTPUT.
     MODULE STATUS_0250 OUTPUT.
       set pf-status 'G250'.
       set TITLEBAR  'T250'.
       PERFORM S250_ALV_INI.
     ENDMODULE.


    *PROCESS AFTER INPUT.
     MODULE USER_COMMAND_0250 INPUT.

      "BREAK-POINT.

      case  FCODE_250.
       when 'BACK'.
         CLEAR FCODE_250.
         LEAVE to screen  0 .
       when 'EXIT'.
         CLEAR FCODE_250.
         LEAVE PROGRAM.
       when 'CANCEL'.
         CLEAR FCODE_250.
         LEAVE to screen  0 .

       when 'REFRESH'.
         CLEAR FCODE_250.
         PERFORM RefreshSelectAmont.

       when 'MAT'.
         CLEAR FCODE_250.
         IF wa_CurrentAct-EDDAT <> '00000000'.
            MESSAGE '当前事务已经被关账过,不可再修改!' type 'E'.
            EXIT.
         ENDIF.

         PERFORM RefreshSelectAmont.
         IF TBX_250_1 <> TBX_250_2.
           MESSAGE '选择的金额不匹配!' type 'E'.
         ELSE.
          data: f_MATNO like ZFIE003_EMAT-MATNO.
          data: wa_BSIS LIKE LINE OF I_BSIS.
          data: wa_ZFIE003_BKDOC LIKE LINE OF I_ZFIE003_BKDOC.

          CALL  FUNCTION 'NUMBER_GET_NEXT'
            EXPORTING
              NR_RANGE_NR         = '01'
              OBJECT          = 'ZSN_MATNO'
              QUANTITY          = '1'
            IMPORTING
              NUMBER          = f_MATNO
            EXCEPTIONS
              INTERVAL_NOT_FOUND    = 1
              NUMBER_RANGE_NOT_INTERN = 2
              OBJECT_NOT_FOUND      = 3
              QUANTITY_IS_0       = 4
              QUANTITY_IS_NOT_1     = 5
              INTERVAL_OVERFLOW     = 6
              BUFFER_OVERFLOW       = 7
              OTHERS          = 8.
          IF SY-SUBRC NE 0.
             MESSAGE '取对账成功确认号错误!' type 'E'.
            EXIT.
          ENDIF.

          LOOP AT I_BSIS INTO wa_BSIS WHERE SELEC = 'X'.
             ZFIE003_EMAT-MATNO = f_MATNO.
             ZFIE003_EMAT-ACTNO = wa_CurrentAct-ACTNO.
             ZFIE003_EMAT-BUKRS = wa_BSIS-BUKRS.
             ZFIE003_EMAT-GJAHR = wa_BSIS-GJAHR.
             ZFIE003_EMAT-BELNR = wa_BSIS-BELNR.
             ZFIE003_EMAT-BUZEI = wa_BSIS-BUZEI.
             insert ZFIE003_EMAT.
          ENDLOOP.

          LOOP AT I_ZFIE003_BKDOC INTO wa_ZFIE003_BKDOC.
             if wa_ZFIE003_BKDOC-SELEC = 'X'.
               ZFIE003_BMAT-MATNO = f_MATNO.
               ZFIE003_BMAT-ACTNO = wa_CurrentAct-ACTNO.
               ZFIE003_BMAT-DOCNO = wa_ZFIE003_BKDOC-DOCNO.
               insert ZFIE003_BMAT.
             endif.
          ENDLOOP.


          IF sy-subrc <> 0.
             ROLLBACK WORK.
             exit.
           else.
             COMMIT WORK.
             PERFORM GetBsisFromDb.
             PERFORM GetBKDocFromDb.
             clear tbx_250_1.
             clear tbx_250_2.
             CALL METHOD grid_250_1->refresh_table_display.
             CALL METHOD grid_250_2->refresh_table_display.
          ENDIF.
         ENDIF.

       when 'SMAT'.
         CLEAR FCODE_250.

         PERFORM GetMatFromDb.

         CALL SCREEN '0350'.

         PERFORM GetBsisFromDb.
         PERFORM GetBKDocFromDb.
         CALL METHOD grid_250_1->refresh_table_display.
         CALL METHOD grid_250_2->refresh_table_display.


       when 'UPL'.
         CLEAR FCODE_250.

         IF wa_CurrentAct-EDDAT <> '00000000'.
            MESSAGE '当前事务已经被关账过,不可再修改!' type 'E'.
            EXIT.
         ENDIF.

         call screen '0010' starting at 30 5 ending at 80 10.

         PERFORM GetBKDocFromDb.
         clear tbx_250_2.
         CALL METHOD grid_250_2->refresh_table_display.

      when 'SNMAT'.
        CLEAR FCODE_250.
        DATA: I_ZFIE003_S_NMAT LIKE ZFIE003_S_NMAT OCCURS 0 WITH HEADER LINE.
        DATA: wa_ZFIE003_S_NMAT LIKE LINE OF I_ZFIE003_S_NMAT,
              wa2_ZFIE003_S_NMAT LIKE LINE OF I_ZFIE003_S_NMAT.
        DATA: ln like ZFIE003_S_NMAT-LINNO,
              ln2 like ZFIE003_S_NMAT-LINNO.



        ln = 1.
    *    IF wa_CurrentAct-EDDAT = '00000000'.
    *        MESSAGE '当前事务未关账,最终打印余额调节表!' type 'W'.
    *        "EXIT.
    *    ENDIF.


        CLEAR I_ZFIE003_S_NMAT[].

        CLEAR wa2_ZFIE003_S_NMAT.
        wa2_ZFIE003_S_NMAT-TEXT_A = '调整后(银行存款)余额'.
        wa2_ZFIE003_S_NMAT-TEXT_B = '调整后(银行对账单)余额'.
        wa2_ZFIE003_S_NMAT-DMBTR_A = wa_CurrentAct-ETBLC.
        wa2_ZFIE003_S_NMAT-DMBTR_B = wa_CurrentAct-BKBLC.



        CLEAR I_ZFIE003_S_NMAT.
        I_ZFIE003_S_NMAT-LINNO = ln.
        I_ZFIE003_S_NMAT-TEXT_A = '银行存款日记账余额'.
        I_ZFIE003_S_NMAT-BLDAT  = wa_CurrentAct-BUDAT.
        I_ZFIE003_S_NMAT-DMBTR_A = wa_CurrentAct-ETBLC.
        I_ZFIE003_S_NMAT-TEXT_B = '银行对账单余额'.
        I_ZFIE003_S_NMAT-BUDAT  = wa_CurrentAct-BLDAT.
        I_ZFIE003_S_NMAT-DMBTR_B = wa_CurrentAct-BKBLC.
        APPEND I_ZFIE003_S_NMAT.
        ln = ln + 1.


        CLEAR I_ZFIE003_S_NMAT.
        CLEAR wa_ZFIE003_S_NMAT.
        I_ZFIE003_S_NMAT-LINNO = ln.
        I_ZFIE003_S_NMAT-TEXT_A = '  加:银行已收,企业未收'.
        "I_ZFIE003_S_NMAT-BLDAT  = ''.
        I_ZFIE003_S_NMAT-DMBTR_A = 0.
        I_ZFIE003_S_NMAT-TEXT_B = '  加:企业已收,银行未收'.
        "I_ZFIE003_S_NMAT-BUDAT  = ''.
        I_ZFIE003_S_NMAT-DMBTR_B = 0.
        MOVE I_ZFIE003_S_NMAT to wa_ZFIE003_S_NMAT. "暂存,求了汇总后再添加

        ln = ln + 1.
        ln2 = ln.

        LOOP AT I_ZFIE003_BKDOC WHERE SHKZG = 'S'.
          CLEAR I_ZFIE003_S_NMAT.
          I_ZFIE003_S_NMAT-LINNO = ln.
          I_ZFIE003_S_NMAT-TEXT_A = I_ZFIE003_BKDOC-PINFO.
          I_ZFIE003_S_NMAT-BLDAT  = I_ZFIE003_BKDOC-BLDAT .
          I_ZFIE003_S_NMAT-DMBTR_A = I_ZFIE003_BKDOC-DMBTR.
          wa_ZFIE003_S_NMAT-DMBTR_A = wa_ZFIE003_S_NMAT-DMBTR_A + I_ZFIE003_S_NMAT-DMBTR_A.
          APPEND I_ZFIE003_S_NMAT.
          ln = ln + 1.
        ENDLOOP.

        LOOP AT I_BSIS WHERE SHKZG = 'S'.
          CLEAR I_ZFIE003_S_NMAT.
          IF ln2 < ln.
            READ TABLE I_ZFIE003_S_NMAT with key LINNO = ln2.
            "I_ZFIE003_S_NMAT-LINNO = ln2.
            I_ZFIE003_S_NMAT-TEXT_B = I_BSIS-SGTXT.
            I_ZFIE003_S_NMAT-BUDAT  = I_BSIS-BLDAT .
            I_ZFIE003_S_NMAT-DMBTR_B = I_BSIS-DMBTR.
            I_ZFIE003_S_NMAT-BELNR = I_BSIS-BELNR.
            I_ZFIE003_S_NMAT-BUZEI = I_BSIS-BUZEI.

            wa_ZFIE003_S_NMAT-DMBTR_B = wa_ZFIE003_S_NMAT-DMBTR_B + I_ZFIE003_S_NMAT-DMBTR_B.
            modify  I_ZFIE003_S_NMAT  TRANSPORTING TEXT_B BUDAT DMBTR_B BELNR BUZEI where LINNO = ln2 .
            ln2 = ln2 + 1.

          ELSE.
            I_ZFIE003_S_NMAT-LINNO = ln.
            I_ZFIE003_S_NMAT-TEXT_B = I_BSIS-SGTXT.
            I_ZFIE003_S_NMAT-BUDAT  = I_BSIS-BLDAT .
            I_ZFIE003_S_NMAT-DMBTR_B = I_BSIS-DMBTR.
            I_ZFIE003_S_NMAT-BELNR = I_BSIS-BELNR.
            I_ZFIE003_S_NMAT-BUZEI = I_BSIS-BUZEI.
            wa_ZFIE003_S_NMAT-DMBTR_B = wa_ZFIE003_S_NMAT-DMBTR_B + I_ZFIE003_S_NMAT-DMBTR_B.
            APPEND I_ZFIE003_S_NMAT.
            ln = ln + 1.
          ENDIF.

        ENDLOOP.

        wa2_ZFIE003_S_NMAT-DMBTR_A = wa2_ZFIE003_S_NMAT-DMBTR_A + wa_ZFIE003_S_NMAT-DMBTR_A.
        wa2_ZFIE003_S_NMAT-DMBTR_B = wa2_ZFIE003_S_NMAT-DMBTR_B + wa_ZFIE003_S_NMAT-DMBTR_B.
        move wa_ZFIE003_S_NMAT to I_ZFIE003_S_NMAT.
        APPEND I_ZFIE003_S_NMAT. "添加汇总行





        CLEAR I_ZFIE003_S_NMAT.
        CLEAR wa_ZFIE003_S_NMAT.
        I_ZFIE003_S_NMAT-LINNO = ln.
        I_ZFIE003_S_NMAT-TEXT_A = '  减:银行已付,企业未付'.
        "I_ZFIE003_S_NMAT-BLDAT  = ''.
        I_ZFIE003_S_NMAT-DMBTR_A = 0.
        I_ZFIE003_S_NMAT-TEXT_B = '  减:企业已付,银行未付'.
        "I_ZFIE003_S_NMAT-BUDAT  = ''.
        I_ZFIE003_S_NMAT-DMBTR_B = 0.
        MOVE I_ZFIE003_S_NMAT to wa_ZFIE003_S_NMAT. "暂存,求了汇总后再添加

        ln = ln + 1.
        ln2 = ln.

        LOOP AT I_ZFIE003_BKDOC WHERE SHKZG = 'H'.
          CLEAR I_ZFIE003_S_NMAT.
          I_ZFIE003_S_NMAT-LINNO = ln.
          I_ZFIE003_S_NMAT-TEXT_A = I_ZFIE003_BKDOC-PINFO.
          I_ZFIE003_S_NMAT-BLDAT  = I_ZFIE003_BKDOC-BLDAT .
          I_ZFIE003_S_NMAT-DMBTR_A = I_ZFIE003_BKDOC-DMBTR.
          wa_ZFIE003_S_NMAT-DMBTR_A = wa_ZFIE003_S_NMAT-DMBTR_A + I_ZFIE003_S_NMAT-DMBTR_A.
          APPEND I_ZFIE003_S_NMAT.
          ln = ln + 1.
        ENDLOOP.

        LOOP AT I_BSIS WHERE SHKZG = 'H'.
          CLEAR I_ZFIE003_S_NMAT.
          IF ln2 < ln.
            READ TABLE I_ZFIE003_S_NMAT with key LINNO = ln2.
            "I_ZFIE003_S_NMAT-LINNO = ln2.
            I_ZFIE003_S_NMAT-TEXT_B = I_BSIS-SGTXT.
            I_ZFIE003_S_NMAT-BUDAT  = I_BSIS-BLDAT .
            I_ZFIE003_S_NMAT-DMBTR_B = I_BSIS-DMBTR.
            I_ZFIE003_S_NMAT-BELNR = I_BSIS-BELNR.
            I_ZFIE003_S_NMAT-BUZEI = I_BSIS-BUZEI.

            wa_ZFIE003_S_NMAT-DMBTR_B = wa_ZFIE003_S_NMAT-DMBTR_B + I_ZFIE003_S_NMAT-DMBTR_B.
            modify  I_ZFIE003_S_NMAT  TRANSPORTING TEXT_B BUDAT DMBTR_B BELNR BUZEI where LINNO = ln2 .
            ln2 = ln2 + 1.

          ELSE.
            I_ZFIE003_S_NMAT-LINNO = ln.
            I_ZFIE003_S_NMAT-TEXT_B = I_BSIS-SGTXT.
            I_ZFIE003_S_NMAT-BUDAT  = I_BSIS-BLDAT .
            I_ZFIE003_S_NMAT-DMBTR_B = I_BSIS-DMBTR.
            I_ZFIE003_S_NMAT-BELNR = I_BSIS-BELNR.
            I_ZFIE003_S_NMAT-BUZEI = I_BSIS-BUZEI.
            wa_ZFIE003_S_NMAT-DMBTR_B = wa_ZFIE003_S_NMAT-DMBTR_B + I_ZFIE003_S_NMAT-DMBTR_B.
            APPEND I_ZFIE003_S_NMAT.
            ln = ln + 1.
          ENDIF.

        ENDLOOP.

        wa2_ZFIE003_S_NMAT-DMBTR_A = wa2_ZFIE003_S_NMAT-DMBTR_A + wa_ZFIE003_S_NMAT-DMBTR_A.
        wa2_ZFIE003_S_NMAT-DMBTR_B = wa2_ZFIE003_S_NMAT-DMBTR_B + wa_ZFIE003_S_NMAT-DMBTR_B.

        move wa_ZFIE003_S_NMAT to I_ZFIE003_S_NMAT.
        APPEND I_ZFIE003_S_NMAT. "添加汇总行

        wa2_ZFIE003_S_NMAT-LINNO = ln .
        move wa2_ZFIE003_S_NMAT to I_ZFIE003_S_NMAT.
        APPEND I_ZFIE003_S_NMAT. "添加汇总行

        sort I_ZFIE003_S_NMAT by LINNO.



        data:BUTXT like t001-BUTXT.

        select SINGLE BUTXT INTO BUTXT From T001 Where BUKRS = p_BUKRS.


    *    DATA: FYEAR LIKE  BKPF-GJAHR,
    *          FMONT LIKE BKPF-MONAT,
        data:       FM_NAME TYPE TDSFNAME.

    *    FYEAR = p_BLDAT-HIGH+0(4).
    *    FMONT = p_BLDAT-HIGH+4(2).

        CALL  FUNCTION 'SSF_FUNCTION_MODULE_NAME'
         EXPORTING
           FORMNAME       = 'ZFIE003_SM_NMAT'
         IMPORTING
           FM_NAME        = FM_NAME
         EXCEPTIONS
           NO_FORM        = 1
           NO_FUNCTION_MODULE = 2
           OTHERS       = 3.
        CASE  SY-SUBRC.
         WHEN 0.
           CALL FUNCTION FM_NAME
           EXPORTING
             STEXT      = wa_CurrentAct-STEXT
             MT         = wa_CurrentAct-BUDAT(6)
             BUTXT      = BUTXT
           TABLES
             NMAT      = I_ZFIE003_S_NMAT
           EXCEPTIONS
             FORMATTING_ERROR   = 1
             INTERVAL_ERROR   = 2
             SEND_ERROR     = 3
             USER_CANCELED    = 4
             MY_EXCEPTION     = 5
             OTHERS       = 6.
         WHEN OTHERS.
           MESSAGE  '窗体调用错误!' type 'E'.
        ENDCASE.

      when 'FINI'.
        CLEAR FCODE_250.


        IF wa_CurrentAct-EDDAT <> '00000000'.
          MESSAGE '当前事务已经被关账过,不可再关账!' type 'E'.
          EXIT.
        ENDIF.

        Data isok(1).
        PERFORM CheckBalance CHANGING isok.

        IF isok = ''.
          MESSAGE '余额调节表不平,请检查...!' type 'E'.
          EXIT.
        ENDIF.


        data:l_answer .
        CALL FUNCTION 'POPUP_TO_CONFIRM_WITH_MESSAGE'
          EXPORTING
            defaultoption = 'N'
            diagnosetext1 = '对当前事务关账,'
            textline1     = '确定继续?'
            titel         = '询问'
          IMPORTING
            answer        = l_answer.

        IF l_answer <> 'J'.
          EXIT.
        ENDIF.

        LOOP AT I_BSIS.
          CLEAR ZFIE003_ENMAT.

          ZFIE003_ENMAT-ACTNO = wa_CurrentAct-ACTNO.
          ZFIE003_ENMAT-BUKRS = I_BSIS-BUKRS.
          ZFIE003_ENMAT-GJAHR = I_BSIS-GJAHR.
          ZFIE003_ENMAT-BELNR = I_BSIS-BELNR.
          ZFIE003_ENMAT-BUZEI = I_BSIS-BUZEI.

          insert ZFIE003_ENMAT.

        ENDLOOP.

        LOOP AT I_ZFIE003_BKDOC.
          CLEAR ZFIE003_BNMAT.
          ZFIE003_BNMAT-ACTNO = wa_CurrentAct-ACTNO.
          ZFIE003_BNMAT-DOCNO = I_ZFIE003_BKDOC-DOCNO.
          INSERT ZFIE003_BNMAT.
        ENDLOOP.

        wa_CurrentAct-EDDAT = sy-datum.
        PERFORM  UpdateCurrentActToDB.

        IF sy-subrc = 0.
          COMMIT WORK.
        else.
          ROLLBACK WORK.
        ENDIF.
        CALL METHOD grid_250_1->refresh_table_display.
        CALL METHOD grid_250_2->refresh_table_display.

      ENDCASE.



     ENDMODULE.


     MODULE OnBudatChanged_0250 INPUT .

      IF FCODE_250 = 'OK'.
        CLEAR FCODE_250.


         IF wa_CurrentAct-EDDAT <> '00000000'.
            MESSAGE '当前事务已经被关账过,不可再修改!' type 'E'.
            EXIT.
         ENDIF.

        PERFORM GetAccountBalance USING wa_CurrentAct-bukrs
                                        wa_CurrentAct-SAKNR
                                        wa_CurrentAct-BUDAT
                                        wa_CurrentAct-WAERS
                               CHANGING wa_CurrentAct-ETBLC.


        PERFORM GetBsisFromDb.

        clear tbx_250_1.
        CALL METHOD grid_250_1->refresh_table_display.
        TBX_250_1 = 0.
        PERFORM UpdateCurrentActToDB.
      ENDIF.
     ENDMODULE.






    "$ Endregion screen 250
    "###############################


    "###############################
    "$ Region screen 0010
    DATA:FCODE_010 LIKE SY-UCOMM.
    DATA:TXT_FILEPATH TYPE  STRING.

    FORM UpLoadBankDoc USING fileName TYPE STRING.
      data: sign(1) type c.
      data: isifr type  i value 0,
         zname(40) type c,
         kawrt_e like konv-kawrt,
         g_lfilename type string,
         isdel(1) type c.

      data: begin of I_BKDOC occurs  0,
          BLDAT   like ZFIE003_BKDOC-bldat,   日期
          DMBTR1  like ZFIE003_BKDOC-DMBTR,   借记金额
          DMBTR2  like ZFIE003_BKDOC-DMBTR,   贷记金额
          BALAC   like ZFIE003_BKDOC-BALAC,   余额
          WAERS   like ZFIE003_BKDOC-WAERS,   货币
          BKITM   like ZFIE003_BKDOC-BKITM,   传票号
          PINFO   like ZFIE003_BKDOC-PINFO,   对方信息
        end of I_BKDOC.

      data:  wa_ZFIE003_BKDOC  LIKE ZFIE003_BKDOC.



      call  function 'GUI_UPLOAD'
       exporting
         filename            = filename
         filetype            = 'ASC'
         has_field_separator       = 'X'
         ignore_cerr           = abap_false
       tables
         data_tab            =  I_BKDOC
       exceptions
         file_open_error         = 1
         file_read_error         = 2
         no_batch            = 3
         gui_refuse_filetransfer     = 4
         invalid_type          = 5
         no_authority          = 6
         unknown_error         = 7
         bad_data_format         = 8
         header_not_allowed      = 9
         separator_not_allowed     = 10
         header_too_long         = 11
         unknown_dp_error        = 12
         access_denied         = 13
         dp_out_of_memory        = 14
         disk_full           = 15
         dp_timeout          = 16
         others            = 17.
      if sy-subrc <> 0.
       message e899(bd) with
         '不能打开文件' fileName '.错误代码:' sy-subrc.
       EXIT.
      ENDIF.

    "BREAK-POINT.
      LOOP  AT I_BKDOC.
       MOVE-CORRESPONDING I_BKDOC TO wa_ZFIE003_BKDOC.

       "取序号码
      CALL  FUNCTION 'NUMBER_GET_NEXT'
       EXPORTING
         NR_RANGE_NR         = '01'
         OBJECT          = 'ZSN_DOCNO'
         QUANTITY          = '1'
       IMPORTING
         NUMBER          = wa_ZFIE003_BKDOC-DOCNO
       EXCEPTIONS
         INTERVAL_NOT_FOUND    = 1
         NUMBER_RANGE_NOT_INTERN = 2
         OBJECT_NOT_FOUND      = 3
         QUANTITY_IS_0       = 4
         QUANTITY_IS_NOT_1     = 5
         INTERVAL_OVERFLOW     = 6
         BUFFER_OVERFLOW       = 7
         OTHERS          = 8.
      IF SY-SUBRC NE 0.
        MESSAGE '取银行对账行号码错误!' type 'E'.
       EXIT.
      ENDIF.

      wa_ZFIE003_BKDOC-SDOCNO = wa_ZFIE003_BKDOC-DOCNO.
      wa_ZFIE003_BKDOC-ACTNO  = wa_CurrentAct-ACTNO.


       IF I_BKDOC-DMBTR1 IS INITIAL AND NOT I_BKDOC-DMBTR2 IS INITIAL.
         wa_ZFIE003_BKDOC-DMBTR = I_BKDOC-DMBTR2.
         wa_ZFIE003_BKDOC-SHKZG = 'S'.
       ENDIF.
       IF I_BKDOC-DMBTR2 IS INITIAL AND NOT I_BKDOC-DMBTR1 IS INITIAL.
         wa_ZFIE003_BKDOC-DMBTR = I_BKDOC-DMBTR1 * ( -1 ).
         wa_ZFIE003_BKDOC-SHKZG = 'H'.
       ENDIF.



       MOVE wa_ZFIE003_BKDOC to ZFIE003_BKDOC.
       INSERT ZFIE003_BKDOC.

       AT LAST.
         wa_CurrentAct-BKBLC = wa_ZFIE003_BKDOC-BALAC.
         wa_CurrentAct-BLDAT = wa_ZFIE003_BKDOC-BLDAT.
         PERFORM UpdateCurrentActToDB.
       ENDAT.
      ENDLOOP.


      if sy-subrc = 0.

       commit work.
       free:wa_ZFIE003_BKDOC,ZFIE003_BKDOC,I_BKDOC,I_BKDOC[].
       MESSAGE '银行对账单导入成功!' type 'I'.
      else.
       rollback work.
      endif.
    endform.




    *PROCESS BEFORE OUTPUT.
     MODULE STATUS_0010 OUTPUT.
       set pf-status 'G010'.
     ENDMODULE.
    *
    *PROCESS AFTER INPUT.
     MODULE USER_COMMAND_0010 INPUT.
     "BREAK-POINT.
      case  FCODE_010.
       when 'OK'.
         PERFORM UpLoadBankDoc USING TXT_FILEPATH.
         CLEAR FCODE_010.
         LEAVE to screen  0 .
       when 'CANCEL'.
         CLEAR FCODE_010.
         LEAVE to screen  0 .
      ENDCASE.

     ENDMODULE.

    module FilePath_F4_0010  input.
    *将本地文件上载到服务器
      call  function 'WS_FILENAME_GET'
       exporting
         def_filename = ' '
         def_path   = ' '
         mask     = ',*.TXT.'
         mode     = 'O'
         title    = '银行对账单导入文件(txt)'
       importing
         filename   = TXT_FILEPATH
       exceptions
         others   = 1.
      check sy-subrc is initial.
    endmodule.                 " FilePath_F4   INPUT








    "$ Endregion screen 0010
    "###############################


    "###############################
    "$ Region Screen 350

    DATA:FCODE_350 like sy-ucomm.

    data: grid_350_1 type  ref to cl_gui_alv_grid,
          grid_350_2 type  ref to cl_gui_alv_grid,
          con_350_1 type ref to   cl_gui_custom_container.

    DATA splitter_350 TYPE REF TO cl_gui_splitter_container.
    DATA container_350_1 TYPE REF TO cl_gui_container.
    DATA container_350_2 TYPE REF TO cl_gui_container.



    form GetMatFromDb .
         SELECT
          ZFIE003_EMAT~MATNO
          ZFIE003_EMAT~ACTNO
          ZFIE003_EMAT~BUKRS
          ZFIE003_EMAT~GJAHR
          ZFIE003_EMAT~BELNR
          ZFIE003_EMAT~BUZEI

          BSIS~MONAT
          BSIS~BLDAT
          BSIS~BUDAT
          BSIS~SHKZG
          BSIS~DMBTR
          BSIS~WAERS
          BSIS~WRBTR
          BSIS~SGTXT
          BSIS~ZUONR

         INTO CORRESPONDING FIELDS OF TABLE I_EMAT

         FROM BSIS
         INNER JOIN ZFIE003_EMAT ON  ZFIE003_EMAT~BUKRS = BSIS~BUKRS
                                 AND ZFIE003_EMAT~GJAHR = BSIS~GJAHR
                                 AND ZFIE003_EMAT~BELNR = BSIS~BELNR
                                 AND ZFIE003_EMAT~BUZEI = BSIS~BUZEI
         WHERE ZFIE003_EMAT~ACTNO = wa_CurrentAct-ACTNO.


         LOOP AT I_EMAT.
            IF I_EMAT-SHKZG = 'H'.
              I_EMAT-dmbtr = - I_EMAT-dmbtr.
              I_EMAT-wrbtr = - I_EMAT-wrbtr.
              modify table I_EMAT.
            ENDIF.
         ENDLOOP.

         SELECT
          ZFIE003_BMAT~MATNO
          ZFIE003_BMAT~ACTNO
          ZFIE003_BMAT~DOCNO
          ZFIE003_BKDOC~BLDAT
          ZFIE003_BKDOC~SHKZG
          ZFIE003_BKDOC~DMBTR
          ZFIE003_BKDOC~BALAC
          ZFIE003_BKDOC~WAERS
          ZFIE003_BKDOC~BKITM
          ZFIE003_BKDOC~PINFO
          ZFIE003_BKDOC~SDOCNO
          ZFIE003_BKDOC~SELEC

         INTO CORRESPONDING FIELDS OF TABLE I_BMAT
         FROM ZFIE003_BMAT
         INNER JOIN ZFIE003_BKDOC ON ZFIE003_BMAT~ACTNO = ZFIE003_BKDOC~ACTNO
                             AND    ZFIE003_BMAT~DOCNO = ZFIE003_BKDOC~DOCNO
         WHERE ZFIE003_BMAT~ACTNO = wa_CurrentAct-ACTNO.



    endform.                    " GetMatFromDb



    FORM S350_ALV_INI.

    data: gt_fieldcat  type lvc_t_fcat.
    data: gs_layout type lvc_s_layo.
    data: gs_variant type disvariant.
    data: gt_toolbar_excluding   Type  UI_FUNCTIONS.



      if con_350_1 is initial.
       create object con_350_1
       exporting container_name = 'CON_350_1'.

       CREATE OBJECT splitter_350
                      EXPORTING parent = con_350_1
                                rows    = 1
                                columns = 2.
       CALL METHOD splitter_350->get_container
                          EXPORTING row      = 1
                                    column   = 1
                          RECEIVING container = container_350_1.

       CALL METHOD splitter_350->get_container
                      EXPORTING row      = 1
                                column   = 2
                      RECEIVING container = container_350_2.


       create object grid_350_1
       exporting
       i_parent = container_350_1.

       create object grid_350_2
       exporting
       i_parent = container_350_2.

    CLEAR gt_toolbar_excluding.
      APPEND:
              cl_gui_alv_grid=>MC_FC_PRINT               to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_DRAG_DROP_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_NO_DELETE_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_LYSTYLE_NO_INSERT_ROWS to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_TO_OFFICE to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_ABC    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CHAIN    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CRBATCH    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_CRWEB    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_LINEITEMS    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_MASTER_DATA    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_MORE    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_REPORT    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_XINT    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CALL_XXL    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_CHECK    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_MB_EXPORT    to gt_toolbar_excluding,

              cl_gui_alv_grid=>MC_FC_GRAPH     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_HELP     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_HTML     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_INFO     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_PC_FILE   to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_VIEWS   to gt_toolbar_excluding,

              cl_gui_alv_grid=>MC_FC_LOC_APPEND_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_COPY     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_COPY_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_CUT     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_DELETE_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_INSERT_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_MOVE_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_PASTE     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_PASTE_NEW_ROW     to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_REFRESH    to gt_toolbar_excluding,
              cl_gui_alv_grid=>MC_FC_LOC_UNDO     to gt_toolbar_excluding
              .




      "$ "$ Region grid1


     "定义grid格式
      clear gs_layout.
      gs_layout-smalltitle  = ''.
      gs_layout-grid_title  = ''.
      gs_layout-zebra   =  'X'."定义GRID的样式如斑马条式
      gs_layout-sel_mode  = 'B'.


      gs_layout-no_toolbar  = ''.


      gs_layout-NO_ROWMARK = 'X'.
      gs_layout-EDIT_MODE = ''.

      "定义列
      DATA: ls_fieldcat like line of gt_fieldcat.
      CLEAR gt_fieldcat[].

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SELEC'.
      ls_fieldcat-coltext = '选择'.
      ls_fieldcat-outputlen = 2.
      ls_fieldcat-checkbox = 'X'.
      "ls_fieldcat-input = 'X'.
      ls_fieldcat-edit = 'X'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'MATNO'.
      ls_fieldcat-coltext = '对账号'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BELNR'.
      ls_fieldcat-coltext = '凭证号'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BUZEI'.
      ls_fieldcat-coltext = '凭证行'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BLDAT'.
      ls_fieldcat-coltext = '凭证日期'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      IF wa_CurrentAct-WAERS = 'RMB'.
        ls_fieldcat-fieldname    = 'DMBTR'.
      ELSE.
        ls_fieldcat-fieldname    = 'WRBTR'.
      ENDIF.
      ls_fieldcat-coltext = '金额'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SGTXT'.
      ls_fieldcat-coltext = '文本'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'ZUONR'.
      ls_fieldcat-coltext = '分配'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'ACTNO'.
      ls_fieldcat-coltext = '事务号'.
      APPEND ls_fieldcat TO gt_fieldcat.

        CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BUKRS'.
      ls_fieldcat-coltext = '公司代码'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'GJAHR'.
      ls_fieldcat-coltext = '会计年度'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'MONAT'.
      ls_fieldcat-coltext = '期间'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BUDAT'.
      ls_fieldcat-coltext = '过账日期'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SHKZG'.
      ls_fieldcat-coltext = '借贷标识'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CONCATENATE sy-repid '350_left' into   gs_variant-report  . "指定保存变式的程序名.
      call  method grid_350_1->set_table_for_first_display
         exporting
          is_variant    = gs_variant
          is_layout      =  gs_layout"采用自定义的格式
          it_toolbar_excluding = gt_toolbar_excluding
          "i_structure_name = 'BSIS'
          I_SAVE = 'A'
          I_DEFAULT = 'X'

         changing
          it_outtab      =  I_EMAT[]
          it_fieldcatalog  = gt_fieldcat[].
          .

     "$ Endregion grid1




      "$ "$ Region grid2


      "定义grid格式
      clear gs_layout.
      gs_layout-smalltitle  = ''.
      gs_layout-grid_title  = ''.
      gs_layout-zebra   =  'X'."定义GRID的样式如斑马条式
      gs_layout-sel_mode  = 'B'.

      gs_layout-no_toolbar  = ''.

      gs_layout-NO_ROWMARK = 'X'.
      gs_layout-EDIT_MODE = ''.


      "定义列


      CLEAR gt_fieldcat[].

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SELEC'.
      ls_fieldcat-coltext = '选择'.
      ls_fieldcat-outputlen = 2.
      ls_fieldcat-checkbox = 'X'.
      "ls_fieldcat-input = 'X'.
      ls_fieldcat-edit = 'X'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'MATNO'.
      ls_fieldcat-coltext = '对账号'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BLDAT'.
      ls_fieldcat-coltext = '日期'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'DMBTR'.
      ls_fieldcat-coltext = '发生金额'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BKITM'.
      ls_fieldcat-coltext = '传票号'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'PINFO'.
      ls_fieldcat-coltext = '对方信息'.
      APPEND ls_fieldcat TO gt_fieldcat.



        CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'DOCNO'.
      ls_fieldcat-coltext = '行号'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'ACTNO'.
      ls_fieldcat-coltext = '事务ID'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'BALAC'.
      ls_fieldcat-coltext = '余额'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'WAERS'.
      ls_fieldcat-coltext = '货币'.
      APPEND ls_fieldcat TO gt_fieldcat.

      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SHKZG'.
      ls_fieldcat-coltext = '借方/贷方标识'.
      APPEND ls_fieldcat TO gt_fieldcat.


      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname    = 'SDOCNO'.
      ls_fieldcat-coltext = '复制行号'.
      APPEND ls_fieldcat TO gt_fieldcat.






      CONCATENATE sy-repid '350_right' into   gs_variant-report  . "指定保存变式的程序名.
      call  method grid_350_2->set_table_for_first_display
         exporting
          is_variant    = gs_variant
          is_layout      =  gs_layout"采用自定义的格式
          it_toolbar_excluding = gt_toolbar_excluding

          I_SAVE = 'A'
          I_DEFAULT = 'X'


         changing
          it_outtab      =  I_BMAT[]
          it_fieldcatalog  = gt_fieldcat[].

     "$ Endregion grid2
      else.

       CALL METHOD grid_350_1->refresh_table_display.
      CALL METHOD grid_350_2->refresh_table_display.

      endif.




    ENDFORM.



    "PROCESS BEFORE OUTPUT.
     MODULE STATUS_0350 OUTPUT.
       set pf-status 'G350'.
       set TITLEBAR  'T350'.
       PERFORM S350_ALV_INI.
     ENDMODULE.


    "PROCESS AFTER INPUT.
     MODULE USER_COMMAND_0350 INPUT.
      case  FCODE_350.
       when 'BACK'.
         Clear FCODE_350.
         LEAVE to screen 0 .
       when 'EXIT'.
         Clear FCODE_350.
         LEAVE PROGRAM.
       when 'CANCEL'.
         Clear FCODE_350.
         leave to screen 0 .
       WHEN 'DEMAT'.

          IF wa_CurrentAct-EDDAT <> '00000000'.
            MESSAGE '当前事务已经被关账过,不可再修改!' type 'E'.
            EXIT.
          ENDIF.

         CALL METHOD grid_350_1->check_changed_data.
         CALL METHOD grid_350_2->check_changed_data.

         LOOP AT I_BMAT WHERE SELEC = 'X'.
           delete from zfie003_bmat where matno = I_BMAT-MATNO.
           delete from zfie003_emat where matno = I_BMAT-MATNO.
         ENDLOOP.

         LOOP AT I_EMAT WHERE SELEC = 'X'.
           delete from zfie003_bmat where matno = I_EMAT-MATNO.
           delete from zfie003_emat where matno = I_EMAT-MATNO.
         ENDLOOP.


         PERFORM GetMatFromDb.
         CALL METHOD grid_350_1->refresh_table_display.
         CALL METHOD grid_350_2->refresh_table_display.


      endcase.
     ENDMODULE.


    "$ Endregion Screen 350
    "###############################

    展开全文
  • 多线程基本原理

    2020-07-03 20:30:53
    继承Thread类,重写run方法 实现Runnable接口 Callable带返回值线程,返回值用Future接收 ThreadPool线程池 实际应用 线程合理利用cpu资源,提高程序吞吐量 比较多实际是线程池,一般不会new一个线程来使用,...

    DougLea 并发设计大师

    为什么需要线程

    单核到多核,达到并行计算,线程是轻量级,成本低,效率高
    合理利用多核cpu资源,提升对计算机资源的利用

    java中如何使用线程

    1. 继承Thread类,重写run方法
    2. 实现Runnable接口
    3. Callable带返回值的线程,返回值用Future接收
    4. ThreadPool线程池

    实际应用

    线程合理利用cpu资源,提高程序的吞吐量
    比较多的实际是线程池,一般不会new一个线程来使用,线程有风险
    使用new Thread造成资源不可控

    如何去应用线程池?
    对账,通过线程进行跑批
    BIO模型优化

    如何改造程序实现异步化处理?

    并发基础

    生命周期
    线程共有六种状态
    在这里插入图片描述
    线程使用目的,提高程序的性能

    java中的线程状态 6种
    线程启动,终止
    interrupt

    锁(保护线程安全)
    synchonic 修饰方法,修饰代码块,修饰实例
    以上方法锁的范围不同。作用范围不同,锁的范围由对象生命周期决定的
    锁的范围,为什么锁的范围会有影响,
    互斥锁的本质:共享资源

    锁,对象锁
    java
    这是jvm源码,这里保存对象头
    在这里插入图片描述
    下面这个就是对象头
    在这里插入图片描述
    轻量级锁,重量级锁,偏向锁,
    加锁会带来性能开销,
    在这里插入图片描述
    偏向锁,轻量级锁,重量级锁

    锁的升级

    在这里插入图片描述
    jdk1.6之前是加重量级锁,会导致性能下降,之后又优化,这里涉及锁的升级。这个是由jvm层面实现的,我们不用管。
    针对不同情况进行优化,提高效率。

    首先偏向锁,没有获取就会升级到轻量级锁,然后重量级锁,阻塞线程
    所以优化就是,在线程阻塞前,抢占锁,轻量级或者偏向锁
    让线程不阻塞,也可以抢占到锁。在不加锁情况解决线程安全问题

    偏向锁(默认关闭,线程没有竞争时候提升性能,真是情况没有必要开启偏向锁,所以一般就是无锁,轻量级锁,重量级锁)
    首先访问同步代码块,看对象头中是有存储了线程id,(上面对象头表中有)如果没有存储,会通过cas操作(CAS,乐观锁,保证原子性,比较并替换,比较预期数据和原始内存中数据是否一致)将其替换掉。当前线程会存储标记,这是在没有竞争的情况下,用偏向锁 如上图 1表示偏向锁,01表示锁的标记,ThreadId会有值。下图总共32位
    在这里插入图片描述

    如果现在有另一个线程访问,cas操作会失败,会撤销原来获得的偏向锁,暂停原来的线程,解锁,将对象头中的线程id设置为空,恢复到无锁状态,恢复线程。
    如果原来线程正在运行,无法撤销,会升级锁为轻量级锁,然后以轻量级锁竞争锁对象

    在这里插入图片描述
    线程栈有Lock record,对象头Object复制到其中,owner指向锁对象
    轻量级锁,栈中存储对象头,通过cas操作,指向占中的指针。当线程2进入,失败,重试机制,自旋锁,多次cas操作(线程获得锁和释放锁时间短),但是也不能一直重试,自适应自旋,会根据上一次抢占时间长短觉定这一次时间多少(底层实现),相比于线程阻塞,挂起的性能好,所以用自旋。如果在指定自旋(自适应自旋)此时还没有获得锁,自旋失败,修改为重量级锁,没有获得锁的线程阻塞,等待唤醒

    重量级锁,队列中排队被阻塞的线程。
    唤醒,进行新一轮的抢占

    非公平锁,允许插队,syncholized

    如何优化,存在竞争就会升级到重量级锁

    生产者和消费者。两个不同的线程
    在这里插入图片描述
    线程通信(wait/notify)等待唤醒
    wait作用,释放当前同步锁,实现线程阻塞
    notify 唤醒被阻塞

    展开全文
  • 第一章 什么是验证?...在这一章,我会介绍验证的基本概念,叙述验证的重要性、代价以及方法。我们会看到不同的测试方法之间的差异,以及测试和验证的差异。我会指出为什么验证是设计复用的关键。

    第一章 什么是验证?

    验证不是一个测试平台,也不是一堆测试平台。验证是一个证明设计[1]的功能是否正确的过程。验证贯穿于我们的日常生活中:对账,品尝炖菜,在地图上寻找标志物。这些都是验证的过程。

    注[1]:设计就是可综合的HDL。

    在这一章,我会介绍验证的基本概念,叙述验证的重要性、代价以及方法。我们会看到不同的测试方法之间的差异,以及测试和验证的差异。我会指出为什么验证是设计复用的关键。

    1.1 什么是测试平台(testbench简称tb)?

    在VHDL和Verilog中,测试平台通常被用于创建一些已知的输入信号到设计中,然后选择性地观察输出信号。测试平台一般用VHDL或Verilog编写,但是,它也包含外部的数据文件或C程序。

    图1-1指出测试平台如何跟待验证设计(DUV)交互。测试平台提供输入信号到设计中,以及观察输出信号。请注意,这是一个完整的闭环系统:没有输入或输出信号[2]。测试平台作用于整个设计。验证的难点就是使用什么样的输入信号作用于设计,以及如何观察输出信号。

    注[2]:在测试平台和DUV以外没有输入或输出。


                                    图1-1

    1.2 验证的重要性。

    如果你看了一些Verilog或VHDL的教材,你会发现里面用很多章节介绍该语言的语法和语义,还有两到三章介绍可综合的代码风格或寄存器传输级(RTL)的子集。

    很多时候,只有一章介绍测试平台,而且是一笔带过的。几乎所有的教材把测试平台局限于运用同步的方式产生简单的输入信号,然后用波形观察工具[3]观察输出信号。大量的篇幅介绍集成开发环境IDE[4],以及语法和语义。

    注[3]:常用的工具有Modelsim。

    注[4]:常用的IDE有Quartus、ISE、Vivado。

    你会觉得学习可综合的VHDL或Verilog代码,比测试平台更重要。事实上,并不是这样的。

    在这个百万门级别的ASIC、可复用知识产权(IP),和SoC的时代,验证占了整个项目的70%工作量。一个开发团队,应该由架构师,验证工程师和设计工程师组成。其中,验证工程师的数量通常是设计工程师的两倍。每当完成项目后,验证的代码占了整个项目代码的80%。

    由于验证的工作量巨大,又缺乏优秀的工程师,而且我们经常在完成设计之后,才开始验证的工作。这也是为什么现在的新工具和新的方法学把验证作为研究的热点。这些工具和方法学通过并行化、高层次抽象和自动化的手段,来缩短验证的时间。

    如果验证可以并行化,那么增加资源可以缩短整个验证的时间。这就好比,很多个工人同时挖一个坑。要让验证并行化,就需要编写可综合HDL代码和测试平台可以同时进行。

    使用高层次抽象可以提高你的工作效率,因为不需要关注底层的细节。正如上面的例子中,使用挖土机来挖坑。

    高层次抽象往往难以控制底层细节,所以要精心选择抽象的层次。高层次抽象常常需要额外的训练来理解抽象机制及其作用。

    使用挖土机挖坑同样会遇到失控的问题:工人不再直接接触泥土,而是使用操控扞和踏板。挖掘的效率大大提高,但是精度降低了,而且只有培训过的工人才能工作。事务、总线周期[5]甚至更高层次,可以使用高层次抽象的方法来验证,而不是和0、1打交道。

          注[5]:总线上的读写操作等。

          自动化可以让电脑自主、快速地完成任务并得到结果,同时你又能处理其它事情。自动化是一个已知输入和输出的标准流程。并非所有的流程都能实现自动化。通用的挖土机是不可能挖出形状、大小、深度、位置和土质条件各不相同的坑。

          验证面临着相同的难题。因为有各种不同的功能、接口、协议和变换要验证,以当今的技术是不可能给出一种通用的、自动的验证方案。但是,我们可以把一部分验证流程自动化,尤其是一些狭窄的应用领域。这就好像挖沟机可以自动挖出放置管道和电缆的线沟。我会介绍各种验证的自动化工具。我希望这本书会帮你在不久的将来认识到全新的、标准的、自动化的验证流程。

    1.3 收敛模型。

    收敛模型从概念上表达了验证的流程。它通常被用来解释验证的对象是什么。

    你必须明确知道你在验证什么。验证的目的就是确定一些变换的结果是否跟预期的一致。例如,对账的目的就是确定所有的交易都被准确无误地记录下来,并且余额能反映出可用资金。


    图1-2

          图1-2说明了变换的验证必需由一个公共起点,再通过两条收敛路径来完成。只要有输入和输出,都可以称之为变换。在一个项目中,规范的RTL代码,插入边界扫描链,把RTL代码综合成门级网表,以及门级网表的版图都是变换。验证使得结果和起点一致。如果变换和验证之间没有公共起点,则没有验证可言。


      图1-3

    图1-3以对账为例描述了收敛模型。在对账中,公共起点就是上个月的结余。变换就是这个月内开出发票,收取发票和还款。验证就是让本月结余和对账单相一致。

    1.4 人为因素。

    如果从端到端的变换没有自动完成,那么就需要个人或小组对变换的实现进行解释。RTL代码就是这样的例子。开发团队先撰写项目计划书,再实现功能正确的可综合代码。通常,每位工程师都要验证代码是否正确。

    图1-4

    图1-4展示了上述情况的收敛模型。如果由同一个人完成需求分析、编写RTL代码及其验证,那么公共的起点就是这个人对需求的理解,但是,不一定是真正的需求。在这种情况下,验证的成败取决于这个人的理解,如果理解有偏差,那么验证就没有意义了。

    人为干预是不确定、不可复制的。可以通过下列方法来减少人为出错的概率:自动化、防差错技术或冗余。

    1.4.1 自动化。

    显然,自动化可以减少人为出错,因为不需要人为干预。但是,并非所有情况都可以使用自动化,尤其是没有明确定义的、需要人类发挥的创造性的时候,例如硬件设计。

    1.4.2 防差错技术。

    把需要人为干预的步骤尽可能地简化、傻瓜化,也是可以减少出错的概率。只需要人类按照特定的步骤去操作,即可得到预期的结果。这种方法在整体品质管理中,叫防差错技术。它离自动化只有一步之遥。然而,它跟自动化一样,需要已知的、标准的变换步骤。时至今日,验证仍然没有固定步骤。

    1.4.3 冗余。

    最后一种减少人为出错的方法是冗余。它是最简单的,却是代价最高的方法。冗余就是让每种变换都复制多份,然后都由不同的人去完成它,再让另一帮人做验证。或者由两个人分别完成两份变换,再去验证他们的结果是否一致。冗余用在要求高可靠性的场合下,比如:航天器。一些工业级的产品,重新设计并替换缺陷品的代价比使用冗余的代价要高得多,比如:IC设计。


    图1-5

    图1-5是个冗余法的收敛模型。对一个模棱两可的需求进行两次解释,可以减少出错的概率。在硬件设计的项目中,编写RTL代码就是变换,然后需要由另一个人做验证。

    1.5 验证什么?

    验证的工具决定了公共起点和收敛点,而起点和收敛点又决定了验证什么。了解这些点的位置有利于理解验证何种变换。形式验证、模型校验、功能验证和测试平台生成器用于验证不同的东西,因为他们的起点和收敛点不同。

    1.5.1 形式验证。

    形式验证起初容易被误解。不熟悉形式验证的工程师经常把它当作利用数学来验证的工具,从而不需要写测试平台。只要你理解形式验证收敛模型的端点,你就知道需要验证什么了。

    形式验证的应用可以分为两大类:等价校验和模型校验。

    1.5.2 等价校验。


    图1-6

    图1-6是等价校验的收敛模型。形式验证利用数学的方法证明了起点和输出在逻辑上是等价的。因此变换保持了它原有的功能。

    等价校验最常见的用法,就是于对比两个网表,以确保处理过的网表是否改变了电路的功能,比如:插入扫描链,综合时钟树,或者是手工修改网表。

    而另一种用法,则是验证RTL代码是否正确地映射成网表。如果可以完全相信综合工具,那就不需要验证了。然而,综合工具是非常庞大的软件,它依赖于算法的正确性和库的信息。历史告诉我们,这样的软件容易出错。而等价校验可以确保综合工具的正确性。

    等价校验也会用于验证两份RTL代码在逻辑上是否一致,目的是为了避免长时间的仿真,例如,少量非功能性的改动会让代码综合出来的效果更好。

    你可以选择等价校验来验证逻辑综合的变换。它只会在功能上对比组合逻辑和时序逻辑,而不关心底层的工艺映射表。

    数字设备公司(DEC)的工程师曾使用等价校验发现设计在综合时出错。这个设计在处理超出48位数据时出错。文档中有明确指出不支持48位以上的数据。但是,综合工具不能理解文档,所以生成了错误的逻辑。等价校验可以快速发现这种用门级仿真难以发现的问题。

    模型校验

    模型校验是形式验证的新应用。它可以从形式上证明断言或设计的特征是否正确。例如,它可以检查出设计中的所有状态机是否存在不可到达的状态。它甚至可以检查出状态机是否死锁。

    与接口相关的断言是另一种形式验证的断言。有关接口的断言,可以使用形式描述语言来描述,然后由工具来证明它是否正确。例如,一个断言可能是:假设ALE信号有效,则DTACK或ABORT信号接着有效。

    图1-7

    图1-7是模型校验的收敛模型。模型校验最大的难点在于如何判断出需要证明哪些断言。在这些断言中,只有一小部分可以被证明的。目前的技术水平还不能证明高层次的断言,即不能确定复杂功能是否被正确地实现。如果能证明以下断言就好了:设置了一组特殊寄存器,异步传输模式(ATM)的单元将会按照某种次序来产生一组输出。可惜,模型校验的技术还没达到这种水平。


    图1-8

    1.5.4 功能验证。

    功能验证目的是确保设计中的功能是否实现。图1-8是功能验证的收敛路径。功能验证检验了设计和规范是否一致。没有功能验证的话,工程师要主观地认为自己完全理解了规范,从规范文档到RTL代码的变换是正确的,

    值得注意的是,除非规范是用非常严谨精确的形式描述语言来描述,否则是不可能证明设计是否符合规范的要求。规范文档是用自然语言写的,工程师们的表达能力各不相同。任何文档都需要理解。功能验证可以说明设计和规范是否一致,但不能证明它。人们可以很轻易地证明设计与规范不一致,但是反过来则很困难。没有人能证明没有不同点。正如没人能证明会飞的驯鹿和UFO不存在。(然而,只要找到一只会飞的驯鹿或一个UFO就能证明它存在了)。

    Testbench生成器。

    日益普及的代码覆盖率和模型验证,有望创造出一种新的验证工具:testbench生成器。

    testbench生成器使用代码覆盖率的指标或者一些证明的结论,再分析源码,然后生成testbench,这样不仅提高代码覆盖率还可以实现设计中随时可变的属性。

    表面上看,这些工具似乎很有吸引力,但是如图1-9所示。它对验证贡献不大。RTL代码是个公共的起点,这里没有收敛点。验证工程师需要自己去判断testbench是否给设计一个有效的激励。可以肯定地说,他必须判断基于此种激励,期望什么样的输出,再去对比设计产生的输出。



    图1-9

    生成的testbench是否有用,我们将在下一章从代码覆盖率的指标来讨论。生成的testbench从模型校验的结果来看是有用,仅仅说明了一个属性可以改变以及什么的输入序列会导致异常。它可能是个鉴别异常条件的工具,如:规范中未考虑的条件或者提供修复问题的调试环境。

    1.6 功能验证的方法。

    功能验证有三种方法:黑盒法,白盒法,灰盒法。

    1.6.1 黑盒法。

    使用黑盒法是不需要关心设计是如何实现的。所有的验证通过接口完成,无需直接询问设计的内部状态,也不需要了解它的结构和实现细节。显然,这种方法缺乏对设计的可控性和可观性。它很难设置一种状态或隔离某些功能。它同样难以观察到输入对应的输出,也难以定位到问题的根源。尤其是输出到产生问题之间有较长的延时就更加难以定位了。

    黑盒法的优点是不需要关心实现的细节。无论设计是用一片ASIC、多片FPGA、一块电路板还是纯软件实现,都是无关重要的。墨盒法是真正的一致性验证,它只检验设计是否实现了规范的要求。

    在一些非常庞大复杂的设计,黑盒法需要一些非功能性的模型来提供额外的可观性和可控性。

    例如,增加一些软件可访问的寄存器来控制或观测内部状态,或者修改处理的数据量让验证耗时最短。这些寄存器不会在正常的操作下使用。他们常常用于初次原型系统的集成阶段。

    黑盒法是唯一一个能与设计并行实现的功能验证方法。因为它不需要关心实现的细节。

    我母亲非常熟悉黑盒法:为了防止我们猜到圣诞节礼物,她从不在礼物盒上写名字。到了圣诞节,她不打开礼物盒,又不得不分辨出每个礼物盒里装的是什么,然后才知道该把礼物给谁。她多次失败后,成为我们的节后笑料。

    1.6.2 白盒法。

    白盒法可以完全地观测和控制设计内部结构和实现的细节。这种方法的优点是可以快速地设置一个感兴趣的状态和输入,或者隔离一个特殊功能。它可以随着验证的进行,很轻易地观测结果,然后马上报告任何异常的行为。

    然而,这种方法要与实现的细节紧密结合。设计一旦修改后,测试平台也要同时修改。

    白盒法是黑盒法有力的补充。这种方法可以确保底层功能的正确性。例如,计数器到达临界值后是否翻转,数据路径是否进行适当的调整和排序。

    1.6.3 灰盒法。

    灰盒法是介于黑盒法和白盒法之间的折中方法。黑盒法不能控制设计,白盒法不可移植。

    与黑盒法类似,灰盒法通过顶层接口来控制和观测设计。同时,针对特定的功能进行验证。相同的验证可以用于不同的设计,但是它不会比黑盒法关注更多的细节。

    1.7 测试和验证。

    测试常常跟验证混淆。测试的目的是检验设计是否正确,验证的目的是确保设计是否实现预期的功能。


    图1-10

    图1-10是验证和测试的收敛模型。测试阶段中,完成的硅片要与生产时提交的网表一致。

    测试是通过测试向量完成的。这些测试向量不是执行函数。它要确保设计中的物理位置能否从0翻转为1以及从1翻转为0。已测的物理位置与总数量之比称为测试覆盖率。测试向量是通过自动测试生成系统(ATPG)自动生成,以达到测试覆盖率最大,同时测试向量最少。

    测试和测试覆盖率取决于设置内部物理位置为1或0的能力,然后观察它们是确实设置了。有些设计只有很少的输入和输出,但有大量可能的状态,需要很长的序列来观察和控制所有内部的物理位置。一个很好的例子是电子手表:它有三或四个输入(拨号盘周围的按钮)和一个输出(显示器上的数字和符号)。如果它带有日历功能,它有十亿个可能的状态(把几百年按毫秒计)。需要几百年才遍历完所有可能的状态。

    1.8 基于扫描的测试。

    有幸的是,基于扫描测试的技术帮助我们去解决这个问题。基于扫描测试将设计中的所有寄存器用一个长长的链串联起来。在正常模式下,操作寄存器仿佛扫描链不存在(如图1-11(a)所示)。在扫描模式下,操作寄存器就像操作一个很长的移位寄存器一样(如图1-11(b)所示)。


    图1-11

    若要测试一个可以扫描的设计,测试单元先进入扫描模式,然后输入信号通过设计内部所有的寄存器进行移位。设计先进入正常模式,再利用单个时钟周期加载正常操作的结果到扫描状态的寄存器。然后设计再次进入扫描模式。结果被移出寄存器(同一时刻把下一个输入移入),并把结果和异常值进行比较。

    这增加了可控性和可观性,同时也增加了测试覆盖率的代价。这限制了设计插入扫描链和自动生成测试用例。这些限制包括但不限于:全同步设计,没有衍生时钟或门控时钟,和使用时钟的单边沿。可测试性设计的主题要大得多而且比这个简单的介绍更复杂。

    硬件工程师最初使用基于扫描的测试时,反对强加给他们的限制。他们只看到眼前的禁区及其最喜爱的设计技巧的不足之处。

    但是,当设计使用一个或多个的扫描链时,按下按钮时自动生成测试用例和实现高测试覆盖率,其价值很快超过了增加的面积和额外设计的开销。节省出来的时间以及增加工程师对产品上市的信心,已经远远超过了基于扫描设计的额外开销。

    1.9 用于验证的设计。

    我们可以修改设计以适应可测试性的需求。但是,修改相同的设计以满足验证的需求是不可行的吗?

    功能验证所付出的努力是设计的两倍,需要额外的设计来简化验证是非常合理的。就像设计使用扫描链以提高可测试性而不增加功能,我们是应该添加非功能的模块和特性以简化验证。这就需要在项目的开始阶段,订制规范阶段就要考虑验证。不仅只有架构师能回答这些问题“这样做的目的是什么?”,还有“如何验证这个东西?”

    典型用于验证的设计技术包括提供额外的软件可访问寄存器以控制和观察内部位置,并提供可编程多路复用器隔离或旁路功能单元。

    1.10 验证和设计重用。

    现在,单芯片可制造的晶体管数量和工程师在合理时间内使用的晶体管数量存在差异。设计重用被认为是克服这种差异的最佳方法。这种差异称为生产率差距。设计重用是一个简单的概念似乎很容易实行。事实证明会产生更多的问题。

    1.11 重用就是信任。

    设计重用的主要障碍是文化。工程师不太乐意使用未知的设计。他们不相信另一个设计是好的或是像自己设计的一样可靠。设计重用的关键是获得信任。

    信任,就像质量一样,是不可以事后添加到设计中的东西。它必须是内置的,并通过尽可能好的设计体现出来。它必须通过可重用设计的后面来获取:提供技术支持和建立客户关系。一旦建立信任,就会更多地重用设计。

    信任也可以通过适当的验证来证明。向客户展示已经根据规范彻底并细致地验证了设计,可以建立信任以及让沟通更流畅。功能验证是唯一的方法来证明设计的质量满足甚至超过了工程师可以自己做的类似的设计。

    1.12 可重用的验证。

    如果你创造了一个设计,你会相信自己的设计能力,并毫无疑问地相信它是正确的。功能验证仅用于确认该意识和加强这些薄弱的意识。如果你要重用一个设计,你只能依靠功能验证来建立同样的信任。因此,可重用设计必须比定制设计更可靠。

    因为可重用设计倾向于可配置和可编程以满足各种应用场合,所以它需要验证所有可能的配置和用途。所有关于可重用性设计的声明必须向客户验证和演示。

    1.13 验证的代价。

    验证是罪恶。它非常耗时,代价很大。验证不会直接产生利润:毕竟,这是经过验证的设计被出售并赚钱,而不是验证。然而验证是必不可少的。要有销路,创造收入,设计必须是功能正确,能满足客户的需求。

    验证没有完成的时候。验证的目的是为了确保设计是对的,但不能证明设计是对的。验证只能显示存在错误,而不是没有错误。只要有足够的时间,就能发现错误。这个问题变成了:这个错误严重到要花多少钱去发现它?随着越来越多的时间花在验证上,以相同的代价来发现的错误越来越少。随着验证的进行,收益递减。它越来越贵了,发现的错误却越来越少,越往后越不可能发现错误。

    功能验证类似于统计学中的假设检验。测试中的假设是:我的设计功能正确吗?这个答案可以是或不是,但是两种答案都可能是错的。这些错误的答案分别是II型和I型错误。


    图1-12

    图1-12显示出现不同类型的错误。I型错误或者叫假阴性,很容易识别出来。验证找到一个不存在的错误。一旦确认这个误解,修改验证把答案从“否”改为“是”,这个错误就不复存在了。第二类错误是最严重的错误:验证不能识别出错误。在II型错误或假阳性情况下,一个糟糕的设计在不知不觉中发货,这会带来不可预估的后果。

    美国食品药品监督管理局面临第二类错误具有潜在毁灭性的后果:尽管临床试验结果阳性,药物被流出在市场上是否危险?糟糕的设计可能导致召回产品或者像太空探测器着陆另一个星球后全部损坏。

    随着公司的未来可能面临潜在的风险,验证中价值64000美元的问题是:验证到什么程度才够?本书所讲述的功能验证和下一章介绍的工具将回答这个问题。

    验证中价值6400万美元的问题是:什么时候才能完成验证?尽管不能精确地估算时间,但确定正在验证哪一步比估算验证所需的时间要容易得多。第三章介绍验证规划,让验证工程师可以更好地估算出完成任务的时间和效果。









    展开全文
  • 请具有基本的 debug 能力!! 欢迎 Star,欢迎 PR! laravel 扩展包请 传送至这里 yii 扩展包请 传送至这里 QQ交流群:690027516 特点 丰富事件系统 命名不那么乱七八糟 隐藏开发者不需要关注细节 根据支付宝...
  • 第一章 会计的基本概念 3 第一节 会计的定义(概念、职能、对象、六要素) 3 第二节 会计核算的基本前提 10 第三节 会计核算信息质量要求 12 第四节 会计方法 15 第二章 会计科目、账户与借贷记账法 18 第一节 会计...
  • _______________ 专业____________________ 班级____________________ 姓名____________________ 学号____________________ 地点____________________ 一、实习要求: 1、掌握原始凭证的基本内容、填制方法及...
  • 08年11月偶书

    2008-11-16 16:24:00
    最近还挺好基本胜任了现在工作,教育局开发还比较顺利,因为我找到了规避复杂开发的方法,在客户端处理符合对账条件记录筛选,这样又不用自己处理复杂的对账过程,我真是还比较适合这种二次开发哦。...
  • 通过财务会计培训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 财务会计实习报告.doc

    2020-12-27 15:24:52
    通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 会计实习总结20XX.doc

    2020-12-27 10:57:20
    通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 会计实训报告.doc

    2021-01-15 14:27:29
    通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 通过财务会计实训,使得我们系统地练习企业会计核算的基本程序和具体方法,加强对所学专业理论知识的理解、实际操作的动手能力,提高运用会计基本技能的水平,也是对所学专业知识的一个检验。通过实际操作,不仅使得...
  • 或者季度对账的时刻了,但是发现工行的企业网银怎么都无法登录,这个时候大部分财务会计的内心都是奔溃的发现无论开机关机多少次,或者是换了多个浏览器都无法登录,直接跳转到该页无法显示,别急,下面的方法基本上...
  • 仓管员工作职责工作内容 仓管员需要具备物流、仓库管理的知识和能力,熟悉货物堆码、掌握货物分类保管及盘点的基本方法;以下是小编精心收集整理的仓管员工作职责,下面小编就和大家分享,来欣赏一下吧。 仓管员工作...
  • 工资发放更是需要细心谨慎,这直接关系到员工个人利益,因为日常工作量已经基本饱和,每发做资时候,我都会主动加班,保证及时将工资发放给员工。 xx年9月我被调至江西xx项目继续担任出纳工作,此项目由于...
  • 【vue】混合模式

    2018-04-23 14:07:00
    因为工作分配,写财务的对账部分,因为3个页面设计和功能基本相同,都是查询筛选表格,所以用混合模式优化了部分代码。用混合把一些共用东西抽离了出来。 具体使用方法参照文档。 ... ...文档是这么介绍:混入...
  • 会计实训心得体会.doc

    2021-01-15 12:33:34
    会计实训心得体会 时间飞逝,转瞬间,咱们已经步入了第三学年,所学内容都是专业课,其中会计实训是十分重要课程,经由前多少学期对基本会计跟企业会计学习,会计实训是由实践到实际操作主要改变。...
  • 会计实习心得范文.doc

    2021-01-15 12:31:50
    会计实习心得范文 会计实习心得体会范文一: 时间飞逝,转瞬间,我们已经步入了第三学年,所学内容都是专业课,其中会计实训是十分重要课程,经由前几学期对基本会计和企业会计学习,会计实训是由理论到实际...
  • 凑数算法

    千次阅读 2011-01-13 15:38:24
    因为他们公司财务对账需要,需要实现一个凑数工具。 具体需求基本就是: 财务出总帐。供应商这里有若干材料单价。需要确定这若干材料数量,使得总价能和财务账面一致。部分材料有约束条件,比如数量不能...
  • 我自觉加强学习,虚心求教释惑,不断理清工作思路,在领导们和同事们的帮助指导下,从不会到会,从不熟悉到熟悉,我逐渐摸清了工作中的基本情况,找到了切入点,把握住了工作重点和难点。一方面,干中学、学中干,...
  • 我自觉加强学习,虚心求教释惑,不断理清工作思路,在领导们和同事们的帮助指导下,从不会到会,从不熟悉到熟悉,我逐渐摸清了工作中的基本情况,找到了切入点,把握住了工作重点和难点。一方面,干中学、学中干,...
  • 我自觉加强学习,虚心求教释惑,不断理清工作思路,在领导们和同事们的帮助指导下,从不会到会,从不熟悉到熟悉,我逐渐摸清了工作中的基本情况,找到了切入点,把握住了工作重点和难点。一方面,干中学、学中干,...
  • 刘强东:死掉创业公司,几乎都违背这 4 点最基本的经济常识 迅雷创始人程浩:AI 创业必知 6 大核心问题,如何选择赛道、搭配团队和应对巨头挑战 马云:阿里必须是一家创造未来公司,必须成为国家和世界创新...

空空如也

空空如也

1 2
收藏数 39
精华内容 15
关键字:

对账的基本方法