有些单据,比如出库单,单据号的规则是这样的:前缀+日期+当日流水码,比如出库单:CK202406230001,入库单: RK 2024 062 3
0001。
还有的是 每周或者每月一个流水 ,规则为 : 前缀 \+ 年月 +当月 流水码,比如 CK 2024 06 0001。
或者 前缀 +当年的第几周 +当周 流水码, 比如 CK2024250001 。
要求一般是允许跳号但是绝对不可以重号。
这种需求我在很多项目上见到过各种不靠谱方案,有的是SELECT
MAX,然后加1,有的用标准的编号范围,然后子对象编号设置365个,从0101到1231,对应一年中的每一天,在取号的时候用月+日为子对象编号。
第一种MAX的方案非常不靠谱就不讨论了,第二个方案看起来虽然很繁琐但是似乎能行得通?但实际上也是有问题的,比如现在是2024年,我想取上一年的数据,20231231这一天的流水码,就没有办法了。还有就是如果设置了主内存缓冲,这个方案占用的缓冲条数太多,容易导致缓冲溢出,所以也不是合适的方案。
我一直使用是自定义的一套流水码方案,以前也有分享过,但是有的同学说在高并发且取号程序不能及时提交的时候,可能会引起取号等待,影响性能,所以今天分享的是一个优化版本,SQL使用
CONNECTION ,立即独立提交,不影响主程序的LUW, 宁可跳号不可以阻塞取号。
本程序能用于R/3 470到最新的S4,基本是能兼容市面上所有的SAP ERP产品了。说起兼容这个事儿,随口提一嘴,有的同学觉着我分享的代码太老气,
好多语法都是CLASSIC的,没有用新语法, 不时髦,,其实这不是我故意写的老气,或者我有老语法癖好,实在是因为需要尽量兼容最多的SAP
ERP版本,不得不这样。
取号示例程序如下,用起来非常简单:
***取已拼接好的当天入库的日流水码DATA rknum(20).CALL FUNCTION 'ZNRIV_GET_NEXT' EXPORTING iv_objct = 'RKNUM' iv_nrlen = 4 iv_prefix = 'RK' IMPORTING ev_prstr = rknum.MESSAGE s000(oo) WITH rknum.
***取指定日期当月的月流水码,仅仅要流水码,不需要拼接DATA cknum TYPE numc06.CALL FUNCTION 'ZNRIV_GET_NEXT' EXPORTING iv_objct = 'CKNUM' iv_datum = '20230601' iv_inter = 'M' IMPORTING ev_srnum = cknum.MESSAGE s000(oo) WITH cknum.
***iv_objct可以随意输入,不需要事先定义
代码和表如下:
FUNCTION znriv_get_next.*"----------------------------------------------------------------------*"*"局部接口:*" IMPORTING*" VALUE(IV_OBJCT) TYPE C*" VALUE(IV_DATUM) TYPE DATUM OPTIONAL*" VALUE(IV_NRLEN) TYPE I OPTIONAL*" VALUE(IV_INTER) DEFAULT 'D'*" VALUE(IV_PREFIX) OPTIONAL*" EXPORTING*" VALUE(EV_SRNUM) TYPE N*" VALUE(EV_PRSTR) TYPE C*"---------------------------------------------------------------------- DATA: lv_kweek TYPE kweek. DATA: ls_dnriv TYPE znriv. DATA: lt_dnriv TYPE TABLE OF znriv. DATA: lv_datum TYPE datum. DATA: lv_daywm TYPE char8. DATA: lo_srnum TYPE REF TO data. DATA: lv_conam TYPE dbcon_name VALUE 'R/3*ZNRIV'. FIELD-SYMBOLS <lv_srnum> TYPE n.
IF ev_srnum IS REQUESTED. "优先级高于iv_nrlen DESCRIBE FIELD ev_srnum LENGTH iv_nrlen IN CHARACTER MODE. ENDIF.
IF iv_nrlen IS INITIAL. MESSAGE e000(oo) WITH '请输入长度或者传入变量'. ENDIF.CREATE DATA lo_srnum TYPE n LENGTH iv_nrlen. ASSIGN lo_srnum->* TO <lv_srnum>.
IF iv_datum IS INITIAL OR iv_datum = ''. iv_datum = sy-datum. ENDIF.
IF iv_inter = 'W'. "每周一个流水 CALL FUNCTION 'GET_WEEK_INFO_BASED_ON_DATE' EXPORTING date = iv_datum IMPORTING week = lv_kweek monday = lv_datum. lv_daywm = lv_kweek. ELSEIF iv_inter = 'M'. "每月一个流水 CONCATENATE iv_datum(6) '01' INTO lv_datum. lv_daywm = iv_datum(6). ELSE. "每天一个流水 lv_datum = iv_datum. lv_daywm = iv_datum. ENDIF.
SELECT SINGLE FOR UPDATE * INTO ls_dnriv FROM znriv CONNECTION (lv_conam) WHERE objct = iv_objct AND datum = lv_datum. IF sy-subrc NE 0. CALL FUNCTION 'ENQUEUE_ESTNRO' EXPORTING object = iv_objct mode_tnro = 'E' EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3. IF sy-subrc = 0. ls_dnriv-objct = iv_objct. ls_dnriv-datum = lv_datum. ls_dnriv-srnum = 0.
DO 365 TIMES. APPEND ls_dnriv TO lt_dnriv. ls_dnriv-datum = ls_dnriv-datum + 1. ENDDO. INSERT znriv CONNECTION (lv_conam) FROM TABLE lt_dnriv ACCEPTING DUPLICATE KEYS.
ls_dnriv-datum = lv_datum. ELSE. MESSAGE e000(oo) WITH '数据初始化中,请重新调用'. ENDIF. ENDIF.
<lv_srnum> = ls_dnriv-srnum = ls_dnriv-srnum + 1. IF <lv_srnum> < ls_dnriv-srnum. MESSAGE e000(oo) WITH '流水码溢出'. ENDIF.UPDATE znriv CONNECTION (lv_conam) FROM ls_dnriv. IF sy-subrc NE 0. MESSAGE e000(oo) WITH '流水码更新数据出错'. ENDIF.
CONCATENATE iv_prefix lv_daywm <lv_srnum> INTO ev_prstr. ev_srnum = <lv_srnum>.COMMIT CONNECTION (lv_conam).ENDFUNCTION.