启用QM功能后MM质检库存数量与QM检验批里管理的质检库存数量不一致的解决思路浅析
作者:袁云飞(AlbertYuan)- 微信号yuanalbert
以下内容均为原创,希望对初学者有一些辅助作用,本人主要从事MM/QM/WM的相关工作,不专业处请多多指点,十足干货,码字不易,且行且珍惜,你们的关注就是我努力的动力,转载请引用出处,感激不尽;
看到这个主题,小伙伴们是不是比较惊讶,关于库存一致性的问题,历来只有MM和FICO之间扯的,毕竟存货金额和科目金额有时就是一对难兄难弟;可这之间库存和检验批质检数量之间也会不一致?不好意思,还真有可能是这样的;
我在一次例行巡检中,碰到了这种比较特例的问题,MB52里的质检库存数量,同未UD检验批的质检数量之和不相等;这种问题非常少见,也比较棘手,因为一旦出现,检验批将不能全部UD掉,因为质检库存不足;
这种问题的原因是比较复杂的,寻找起来非常困难,由于系统更新问题,表写入时间差等各种主观客观情况,确认问题点变得异常困难,而且往往出现这种问题的时候就是比较紧急的,工厂可能立即需要使用这批东西,库存得马上改变状态,而使用了QM之后不能使用MM的方式去在库存类型之间进行切换(关联检验批的情况);所以我们这里着重探讨如何去解决这个问题的思路和方法;
有幸的是SAP给我们提供了一个预防性检查的报表程序,并且该程序可以在找到不一致的情况下进行对应的处置;看来SAP是提前就知道有这么一回事了,而且根本性修复估计不太现实,所以他提供这种方案,为什么?因为这个补丁程序在几乎所有SAP版本里都适用,包括S/4 HANA 101-103;原因不言自明了吧(* ̄︶ ̄)
两个办法,一个是使用补丁程序NOTE 48815完成程序生成;另一个就是拷贝我后面贴出来的代码创建对应的程序即可;
打补丁的前置条件如下:
先用SE38创建一个程序ZQEVAC20,然后程序类型选择1,应用选择Q,然后将程序激活,并将产生的传输请求号释放一下;这样打补丁的时候就不会报错了;
补丁打完后,程序代码就会填充到这个创建的程序里,我们只需要修改一下英文的查询条件描述即可使用;(打NOTE不用我教小伙伴们了吧,这个比较简单的,BASIS可以做,MM也可以做,让BASIS帮忙做一下吧,毕竟他们更专业)
程序被执行后,如果发现了库存不一致,则就会输出一个清单,包括当前质检库存数量,以及按检验批汇总的数量;如果有不一致,则单击输出清单第一列,系统会自动执行修正和保存;
系统自动修正的逻辑如下:
1,MM的质检库存数量大于检验批汇总的质检数量,则后台会自动执行一个321的质检到非限制的移动,表示不需要检验批管理的那部分MM质检库存可以直接释放到非限制库存里;2,MM的质检库存小于了QM检验批的质检数量,系统会搜索一个用于进行数量调整的检验批,如果多个检验批被找到,系统会让用户进行选择;
如果报表没有发现任何不一致性,但库存概览仍然显示检验库存,并且无法在MM中执行转移过帐,在QVM2中检查是否仍存在开放数量的检验批。如果存在这样的检验批次,则使用QVM2记账从检验库存中的总数量即可。
下面是S/4 HANA里该报表的源代码:
*$*$----------------------------------------------------------------$*$*
*$ Correction Inst. 0020751258 0000223522 $*
*$--------------------------------------------------------------------$*
*$ Valid for : $*
*$ Software Component S4CORE $*
*$ Release 101 All Support Package Levels $*
*$ Release 102 All Support Package Levels $*
*$ Release 103 All Support Package Levels $*
*$--------------------------------------------------------------------$*
*$ Changes/Objects Not Contained in Standard SAP System $*
*$*$----------------------------------------------------------------$*$*
*&--------------------------------------------------------------------*
*& Object REPS ZQEVAC20
*& Object Header PROG ZQEVAC20
*&--------------------------------------------------------------------*
*& REPORT ZQEVAC20
*&--------------------------------------------------------------------*
*>>>> START OF INSERTION <<<<
REPORT zqevac20 LINE-SIZE 255 NO STANDARD PAGE HEADING.
*&---------------------------------------------------------------------*
*& Report ZQEVAC20
*&
*&---------------------------------------------------------------------*
* Check programm: compare inspection lot quantity via Q-stock quantity *
*&---------------------------------------------------------------------*
TABLES: qmat,
jest,
mara,
marc,
mard,
mkol,
mska,
mspr,
mchb,
qals,
t001l.
*----------------------------------------------------------------------*
INCLUDE <icon>.
CONSTANTS:
c_prueflos LIKE qals-prueflos VALUE '999999999999',
c_q_best LIKE qals-insmk VALUE 'X',
c_kreuz LIKE qm00-qkz VALUE 'X'.
*----------------------------------------------------------------------*
SELECT-OPTIONS material FOR qmat-matnr MATCHCODE OBJECT mat1.
SELECT-OPTIONS werk FOR qmat-werks.
*----------------------------------------------------------------------*
DATA:
* inspection types
g_insp_types LIKE tq32 OCCURS 0,
* Table of materials
BEGIN OF g_mattab OCCURS 0,
matnr LIKE qmat-matnr,
werks LIKE qmat-werks,
END OF g_mattab,
* Table of quantities
BEGIN OF g_besttab OCCURS 0,
matnr LIKE qals-matnr,
werks LIKE qals-werkvorg,
lgort LIKE qals-lagortvorg,
charg LIKE qals-charg,
sobkz LIKE qals-sobkz,
lifnr LIKE qals-lifnr, "für SOBKZ = K
pspnr LIKE qals-ps_psp_pnr, "für SOBKZ = Q
kdauf LIKE qals-kdauf, "für SOBKZ = E
kdpos LIKE qals-kdpos, "für SOBKZ = E
mm_menge LIKE qals-lmengezub,
qm_menge LIKE qals-lmengezub,
meins LIKE qals-mengeneinh,
prueflos LIKE qals-prueflos, "early inspection
ok LIKE qm00-qkz,
END OF g_besttab,
* storage locations
g_sloc_tab TYPE STANDARD TABLE OF t001l,
gv_ewm_active TYPE /spe/ctrl_is_active,
*
g_field(30),
g_new_icon LIKE icon_checked VALUE icon_checked,
g_ok LIKE qm00-qkz,
g_data_available LIKE qm00-qkz,
*
g_listi LIKE sy-listi,
g_lilli LIKE sy-lilli,
*
end_of_data.
*----------------------------------------------------------------------*
************************************************************************
START-OF-SELECTION.
* Make sure that the report is running only once in a system
DATA:
l_message LIKE sy-msgv1 VALUE 'Report is in usage by &'."#EC NOTEXT
CALL FUNCTION 'ENQUEUE_EQQALS1'
EXPORTING
prueflos = c_prueflos
EXCEPTIONS
foreign_lock = 1.
IF NOT sy-subrc IS INITIAL.
REPLACE '&' WITH sy-msgv1 INTO l_message.
MESSAGE i208(00) WITH l_message.
SUBMIT (sy-repid) VIA SELECTION-SCREEN.
ENDIF.
* possible inxpection types
SELECT * INTO TABLE g_insp_types FROM tq32
WHERE herkunft IN ('01', '04', '05', '08', '09').
* read QMAT
SELECT DISTINCT matnr werks
INTO CORRESPONDING FIELDS OF TABLE g_mattab
FROM qmat
FOR ALL ENTRIES IN g_insp_types
WHERE matnr IN material
AND werks IN werk
AND aktiv = c_kreuz
AND art = g_insp_types-art
AND afr = space. "HUM
* read storage locations
IF NOT g_mattab[] IS INITIAL.
SELECT * FROM t001l INTO t001l
FOR ALL ENTRIES IN g_mattab
WHERE werks = g_mattab-werks.
* check stock is not EWM relevant
gv_ewm_active =
/spe/cl_ctrl=>is_ewm( i_plant = t001l-werks
i_stloc = t001l-lgort ).
* add stock into table
* in case it is not HU revant and not EWM relevant
IF t001l-xhupf IS INITIAL
AND gv_ewm_active IS INITIAL.
APPEND t001l TO g_sloc_tab.
ENDIF.
ENDSELECT.
ENDIF.
*
LOOP AT g_mattab.
*
REFRESH g_besttab. CLEAR g_besttab.
*----------------------------------------------------------------------*
* Q-stock of materials
IF NOT g_sloc_tab[] IS INITIAL.
PERFORM mm_best_lesen.
ENDIF.
*----------------------------------------------------------------------*
* quantities of inspection lots
PERFORM lose_lesen.
* evaluate lots
LOOP AT g_besttab.
IF g_besttab-mm_menge NE g_besttab-qm_menge.
* different quatity. write info
WRITE:/(3) icon_change AS ICON HOTSPOT,
4 g_besttab-matnr,
g_besttab-werks,
g_besttab-lgort,
g_besttab-charg,
g_besttab-sobkz,
g_besttab-mm_menge, "#EC UOM_IN_MES
g_besttab-qm_menge, "#EC UOM_IN_MES
g_besttab-lifnr,
g_besttab-pspnr,
g_besttab-kdauf,
g_besttab-kdpos NO-ZERO.
HIDE: g_besttab.
ENDIF.
ENDLOOP.
ENDLOOP.
* Initialize workarea
CLEAR g_besttab.
************************************************************************
END-OF-SELECTION.
IF g_data_available IS INITIAL.
WRITE: /20 icon_checked AS ICON,
'No differences between QM quantities and' NO-GAP, "#EC NOTEXT
' MM quatities found'. "#EC NOTEXT
ENDIF.
************************************************************************
TOP-OF-PAGE.
ULINE.
WRITE:/' Material Plnt SLoc Batch ' NO-GAP,"#EC NOTEXT
'S MM-Quantity QM-Quantity ' NO-GAP, "#EC NOTEXT
'Vendor Project Customer order'. "#EC NOTEXT
ULINE.
MOVE c_kreuz TO g_data_available.
************************************************************************
AT LINE-SELECTION.
MOVE: sy-listi TO g_listi,
sy-lilli TO g_lilli.
GET CURSOR FIELD g_field.
CHECK g_field EQ 'ICON_CHANGE'.
CHECK g_besttab-ok IS INITIAL.
* select line -> g_besttab filled
IF g_besttab-mm_menge LT g_besttab-qm_menge.
* more QM quantity
PERFORM lose_korrigieren USING g_besttab-ok.
ELSE.
* more MM quantity
PERFORM bestand_korrigieren USING g_besttab-ok.
ENDIF.
IF g_besttab-ok NE space.
*
READ LINE g_lilli INDEX g_listi.
MOVE 'X' TO g_besttab-ok.
* MODIFY LINE G_LILLI INDEX G_LISTI
* FIELD VALUE G_BESTTAB.
* change Icon to ok
TRANSLATE g_new_icon USING '@ '.
CONDENSE g_new_icon NO-GAPS.
MODIFY LINE g_lilli INDEX g_listi
FIELD VALUE icon_change FROM g_new_icon.
ENDIF.
CLEAR g_besttab.
************************************************************************
*&---------------------------------------------------------------------*
*& Form LOSE_LESEN
*&---------------------------------------------------------------------*
* get the lots
*----------------------------------------------------------------------*
FORM lose_lesen.
DATA: l_objnr LIKE qals-objnr,
l_inact LIKE jest-inact,
l_xchpf LIKE qals-xchpf,
l_lifnr LIKE qals-lifnr,
l_kdauf LIKE qals-kdauf,
l_kdpos LIKE qals-kdpos,
l_pspnr LIKE qals-ps_psp_pnr,
l_stat18 LIKE qals-stat18,
l_prueflos LIKE qals-prueflos.
CLEAR g_besttab.
*
SELECT matnr werkvorg lagortvorg charg sobkz objnr
lifnr ps_psp_pnr kdauf kdpos lmengezub xchpf
stat18 prueflos
INTO (g_besttab-matnr,g_besttab-werks,
g_besttab-lgort,g_besttab-charg,
g_besttab-sobkz,l_objnr,
l_lifnr,l_pspnr,l_kdauf,l_kdpos,
g_besttab-qm_menge,l_xchpf,
l_stat18,l_prueflos) FROM qals
FOR ALL ENTRIES IN g_sloc_tab
WHERE matnr EQ g_mattab-matnr
AND werkvorg EQ g_mattab-werks
AND werkvorg EQ g_sloc_tab-werks
AND stat01 EQ c_kreuz
AND insmk EQ c_q_best
AND stat34 EQ space
AND lmengezub GT 0
AND lagortvorg EQ g_sloc_tab-lgort.
SELECT SINGLE inact FROM jest INTO l_inact
WHERE objnr EQ l_objnr
AND stat EQ 'I0203'
AND inact EQ space.
* lot only relevant with active JEST-entry which is not active
IF sy-subrc IS INITIAL.
SELECT SINGLE inact FROM jest INTO l_inact
WHERE objnr EQ l_objnr
AND stat EQ 'I0220'
AND inact EQ space.
IF NOT sy-subrc IS INITIAL.
* lot quantity is not comlete posted (redundant to STAT34)
* special stock ?
CLEAR: g_besttab-lifnr,
g_besttab-pspnr,
g_besttab-kdauf,
g_besttab-kdpos.
CASE g_besttab-sobkz.
WHEN space.
WHEN 'K'. MOVE l_lifnr TO g_besttab-lifnr.
WHEN 'Q'. MOVE l_pspnr TO g_besttab-pspnr.
WHEN 'E'.
MOVE l_kdauf TO g_besttab-kdauf.
MOVE l_kdpos TO g_besttab-kdpos.
ENDCASE.
IF l_stat18 EQ 'Y' "early insp.lot without batch
AND l_xchpf NE space
AND g_besttab-charg IS INITIAL .
MOVE l_prueflos TO g_besttab-prueflos.
ELSE.
CLEAR g_besttab-prueflos.
ENDIF.
COLLECT g_besttab.
ENDIF.
ENDIF.
ENDSELECT.
ENDFORM. " LOSE_LESEN
*&---------------------------------------------------------------------*
*& Form MM_BEST_LESEN
*&---------------------------------------------------------------------*
* Quantity per Material/Plant
*----------------------------------------------------------------------*
FORM mm_best_lesen.
DATA:
l_xchar LIKE marc-xchar.
SELECT SINGLE xchar INTO (l_xchar) FROM marc
WHERE matnr EQ g_mattab-matnr
AND werks EQ g_mattab-werks.
IF l_xchar IS INITIAL.
* not batch controlled
CLEAR g_besttab.
MOVE-CORRESPONDING g_mattab TO g_besttab.
SELECT insme lgort INTO (g_besttab-mm_menge,g_besttab-lgort)
FROM mard
FOR ALL ENTRIES IN g_sloc_tab
WHERE matnr EQ g_mattab-matnr
AND werks EQ g_mattab-werks
AND werks EQ g_sloc_tab-werks
AND lgort EQ g_sloc_tab-lgort.
APPEND g_besttab.
ENDSELECT.
ELSE.
* Batch controlled
CLEAR g_besttab.
MOVE-CORRESPONDING g_mattab TO g_besttab.
SELECT cinsm lgort charg
INTO (g_besttab-mm_menge,g_besttab-lgort,
g_besttab-charg)
FROM mchb
FOR ALL ENTRIES IN g_sloc_tab
WHERE matnr EQ g_mattab-matnr
AND werks EQ g_mattab-werks
AND werks EQ g_sloc_tab-werks
AND lgort EQ g_sloc_tab-lgort.
APPEND g_besttab.
ENDSELECT.
ENDIF.
* consignment stock
CLEAR g_besttab.
MOVE-CORRESPONDING g_mattab TO g_besttab.
SELECT sinsm lgort charg sobkz lifnr
INTO (g_besttab-mm_menge,g_besttab-lgort,
g_besttab-charg,g_besttab-sobkz,g_besttab-lifnr)
FROM mkol
FOR ALL ENTRIES IN g_sloc_tab
WHERE matnr EQ g_mattab-matnr
AND werks EQ g_mattab-werks
AND werks EQ g_sloc_tab-werks
AND lgort EQ g_sloc_tab-lgort.
APPEND g_besttab.
ENDSELECT.
* sales order stock
CLEAR g_besttab.
MOVE-CORRESPONDING g_mattab TO g_besttab.
SELECT kains lgort charg sobkz vbeln posnr
INTO (g_besttab-mm_menge,g_besttab-lgort,
g_besttab-charg,g_besttab-sobkz,
g_besttab-kdauf,g_besttab-kdpos)
FROM mska
FOR ALL ENTRIES IN g_sloc_tab
WHERE matnr EQ g_mattab-matnr
AND werks EQ g_mattab-werks
AND werks EQ g_sloc_tab-werks
AND lgort EQ g_sloc_tab-lgort.
APPEND g_besttab.
ENDSELECT.
* project stock
CLEAR g_besttab.
MOVE-CORRESPONDING g_mattab TO g_besttab.
SELECT prins lgort charg sobkz pspnr
INTO (g_besttab-mm_menge,g_besttab-lgort,
g_besttab-charg,g_besttab-sobkz,g_besttab-pspnr)
FROM mspr
FOR ALL ENTRIES IN g_sloc_tab
WHERE matnr EQ g_mattab-matnr
AND werks EQ g_mattab-werks
AND werks EQ g_sloc_tab-werks
AND lgort EQ g_sloc_tab-lgort.
APPEND g_besttab.
ENDSELECT.
ENDFORM. " MM_BEST_LESEN
*&---------------------------------------------------------------------*
*& Form LOSE_KORRIGIEREN
*&---------------------------------------------------------------------*
* Reduce open quantity for lot and update
*----------------------------------------------------------------------*
FORM lose_korrigieren USING p_ok LIKE qm00-qkz.
DATA:
l_subrc LIKE sy-subrc,
l_meins LIKE mara-meins,
l_kunnr LIKE qals-kunnr, "Dummy
l_difmg LIKE qals-lmengezub.
*
CLEAR p_ok.
* get unit of measure
PERFORM hole_mara_meins USING g_besttab-matnr
CHANGING l_meins.
l_difmg = g_besttab-qm_menge - g_besttab-mm_menge.
* -> subtract
CALL FUNCTION 'QELA_LOT_QUANTITY_CORRECTIONS'
EXPORTING
i_matnr = g_besttab-matnr
i_charg = g_besttab-charg
i_difmg = l_difmg
i_meins = l_meins
i_werks = g_besttab-werks
i_lgort = g_besttab-lgort
i_sobkz = g_besttab-sobkz
i_kdauf = g_besttab-kdauf
i_kdpos = g_besttab-kdpos
i_lifnr = g_besttab-lifnr
i_ps_psp_pnr = g_besttab-pspnr
i_kunnr = l_kunnr
i_insp_lot = g_besttab-prueflos
* I_WINX1_30 = 2
* I_WINX2_30 = 75
* I_WINY1_30 = 5
* I_WINY2_30 = 15
IMPORTING
e_subrc = l_subrc.
* EXCEPTIONS
* no_authority = 1.
IF NOT sy-subrc IS INITIAL. "#EC *
MESSAGE ID sy-msgid TYPE 'I' NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
EXIT.
ENDIF.
CASE l_subrc.
WHEN 0.
" Nothing to do
WHEN 4.
MESSAGE I164(QA).
WHEN 8.
MESSAGE I077(QA) WITH g_besttab-matnr
g_besttab-charg
g_besttab-werks
g_besttab-lgort.
WHEN 12.
MESSAGE I078(QA) WITH g_besttab-matnr
g_besttab-charg
g_besttab-werks
g_besttab-prueflos.
WHEN OTHERS.
ENDCASE.
* if o.k. -> save !
IF l_subrc IS INITIAL.
COMMIT WORK AND WAIT.
MOVE c_kreuz TO p_ok.
ENDIF.
ENDFORM. " LOSE_KORRIGIEREN
*&---------------------------------------------------------------------*
*& Form HOLE_MARA_MEINS
*&---------------------------------------------------------------------*
* Get unit
*----------------------------------------------------------------------*
* --> P_MATNR
* <-- P_MEINS
*----------------------------------------------------------------------*
FORM hole_mara_meins USING p_matnr
CHANGING p_meins.
STATICS:
s_matnr LIKE mara-matnr,
s_meins LIKE mara-meins.
IF p_matnr NE s_matnr.
MOVE p_matnr TO s_matnr.
SELECT SINGLE meins INTO (s_meins) FROM mara
WHERE matnr EQ s_matnr.
ENDIF.
MOVE s_meins TO p_meins.
ENDFORM. " HOLE_MARA_MEINS
*&---------------------------------------------------------------------*
*& Form bestand_korrigieren
*&---------------------------------------------------------------------*
* Special routine for quantity correction in quality stock
*----------------------------------------------------------------------*
FORM bestand_korrigieren USING p_ok LIKE qm00-qkz.
DATA:
l_meins LIKE mara-meins,
l_difmg LIKE qals-lmengezub,
l_rmqed LIKE rmqed,
l_qals LIKE qals.
*
CLEAR p_ok.
*
l_difmg = g_besttab-mm_menge - g_besttab-qm_menge.
* get unit of measure
PERFORM hole_mara_meins USING g_besttab-matnr
CHANGING l_meins.
*
DATA:
l_text(80),
l_imkpf LIKE imkpf,
l_imseg LIKE imseg,
l_imsegtab LIKE imseg OCCURS 1,
l_emkpf LIKE emkpf,
l_emseg LIKE emseg,
l_emsegtab LIKE emseg OCCURS 1.
* material doc. header
MOVE : sy-datum TO l_imkpf-bldat,
sy-datum TO l_imkpf-budat,
'Correction doc. through QM'(111) TO l_imkpf-bktxt."#EC NOTEXT
*
* Canc÷l-posting for quality -> unrestricted (check TQ07M)
MOVE: '321' TO l_imseg-bwart.
* additional fields
MOVE : g_besttab-matnr TO l_imseg-matnr,
g_besttab-werks TO l_imseg-werks,
l_meins TO l_imseg-erfme,
l_difmg TO l_imseg-erfmg,
g_besttab-charg TO l_imseg-charg,
g_besttab-lgort TO l_imseg-lgort,
g_besttab-sobkz TO l_imseg-sobkz,
g_besttab-lifnr TO l_imseg-lifnr,
g_besttab-pspnr TO l_imseg-mat_pspnr,
g_besttab-kdauf TO l_imseg-mat_kdauf,
g_besttab-kdpos TO l_imseg-mat_kdpos.
*
APPEND l_imseg TO l_imsegtab.
*
* Temporary deactivation of QM in MM !
CALL FUNCTION 'QAAT_QM_ACTIVE_INACTIVE'
EXPORTING
aktiv = space.
CALL FUNCTION 'MB_CREATE_GOODS_MOVEMENT'
EXPORTING
imkpf = l_imkpf
xallp = c_kreuz
xallr = c_kreuz
ctcod = 'MB11'
xqmcl = c_kreuz
IMPORTING
emkpf = l_emkpf
TABLES
imseg = l_imsegtab
emseg = l_emsegtab.
* Reactivation of QM in MM !
CALL FUNCTION 'QAAT_QM_ACTIVE_INACTIVE'
EXPORTING
aktiv = c_kreuz.
* Check posting
IF NOT l_emkpf-subrc LE 1.
IF NOT l_emkpf-msgno IS INITIAL.
MESSAGE ID l_emkpf-msgid TYPE 'I' NUMBER l_emkpf-msgno
WITH l_emkpf-msgv1 l_emkpf-msgv2 l_emkpf-msgv3 l_emkpf-msgv4.
* Error
EXIT.
ENDIF.
LOOP AT l_emsegtab INTO l_emseg.
IF NOT l_emseg-msgno IS INITIAL.
* Error in stock posting
MESSAGE ID l_emseg-msgid TYPE 'I' NUMBER l_emseg-msgno
WITH l_emseg-msgv1 l_emseg-msgv2 l_emseg-msgv3 l_emseg-msgv4.
* Error
EXIT.
ENDIF.
ENDLOOP.
ELSE.
* o.k., -> save !
CALL FUNCTION 'MB_POST_GOODS_MOVEMENT'.
COMMIT WORK AND WAIT.
MOVE c_kreuz TO p_ok.
ENDIF.
ENDFORM. " bestand_korrigieren.
.
*>>>> END OF INSERTION <<<<<<
...
*&--------------------------------------------------------------------*