SAP 發票合併與拆分

問題的提出:
很多企業在晚上運行後臺作業來開具到期發票。問題是這些自動運行的到期清單,是以怎樣的邏輯被拆分、合併的?本文檔將解釋標準系統的工作邏輯並提供事例來說明怎樣對標準邏輯作自定義增強。
發票(Billing Documents)是用功能函數RV_INVOICE_CREATE來創建的,這個函數將被VF01/SAPMV60A、VF04/SDBILLDL等事務碼調用。在此函數運行之前,會有一個內含一條或者多條數據的內表傳入。這些數據將以客戶、銷售組織、開票類型等字段排序,排序的結果是這些數據被分成了幾個小組。對每一個小組,系統邏輯試圖合併他們從而開除一張發票。在發票創建的過程中,系統將從上述內表中提取出擡頭和行項目數據,填充至帶開票的內存數據中。
r3中包含了一種用於數據傳輸控制的可配置的函數邏輯(Routines),叫Data Transfer Routine,它可以更改數據從源憑證到目的憑證的傳遞方式。本例中將對發票憑證進行討論。
一旦待開票的數據(內存數據)被填充,系統邏輯將循環對照次行數據與前行數據,如果基本字段一致,兩者將被合併。如果不同,新的發票將被創建。發票裏有幾個特殊的字段,他們不能作爲拆分依據。下一節將說明標準合併規則(Standard Combination Criteria),裏面列舉了這些不能作爲拆分依局的字段。
標準合併規則(Standard Combination Criteria)
程序SAPMV60A/MV60ATOP中,有如下定義:
DATA: BEGIN OF AUSNAHME_TAB,
        FELD1(5)  VALUE 'VBELN',
        FELD2(5)  VALUE 'NETWR',
        FELD3(5)  VALUE 'KNUMV',
        FELD4(5)  VALUE 'VBELN',
        FELD5(5)  VALUE 'ERNAM',
        FELD6(5)  VALUE 'ERDAT',
        FELD7(5)  VALUE 'ERZET',
        FELD8(5)  VALUE 'AEDAT',
        FELD9(5)  VALUE 'BELNR',
        FELD10(5) VALUE 'RFBSK',
        FELD12(5) VALUE 'SFAKN',
        FELD13(5) VALUE 'MWSBK',
        FELD14(5) VALUE 'FKTYP',
      END OF AUSNAHME_TAB.
存在於AUSNAHME_TAB結構中的字段將不會作爲拆分依據。具體判斷邏輯在函數SPLIT_TREE_LIST_DISPLAY、子例程SPLIT_EXAMINE中:
        IF I_HEADER_EXCEPTION_FIELDS NS GT_HEADER_NAMETAB-FIELDNAME.
          GT_HEADER_FIELD_SPLIT-VBELN_F  = LS_VBRK_F-VBELN.
          GT_HEADER_FIELD_SPLIT-VBELN_S  = LS_VBRK_S-VBELN.
          GT_HEADER_FIELD_SPLIT-FIELD    = GT_HEADER_NAMETAB-FIELDTEXT.
          GT_HEADER_FIELD_SPLIT-VALUE_F  = .
          GT_HEADER_FIELD_SPLIT-VALUE_S  = .
          APPEND GT_HEADER_FIELD_SPLIT.
        ENDIF.
數據傳輸例程(Data Transfer Routine)
正確的控制合併和拆分的方法是採用數據傳輸例程(Data Transfer Routines)。數據傳輸例程的主要目的是將前導文件數據傳到待創建的後續文件的內存結構中。我們可以在VOFM中找到系統標準的例程。
在VOFM裏定義自己的邏輯後,再在發票拷貝關係(TCode:VTFL)中分配給相關的單據。
1、強制拆分
在發票擡頭中有一個用來強制拆分開票的特殊字段:VBRK-ZUKRI。如001例程中,SPART字段和VTWEG字段分別代表產品組合分銷渠道。所以,001例程的含義就是,不同的產品組、不同的分銷渠道,要出具不同的發票!
FORM DATEN_KOPIEREN_900.
  DATA: BEGIN OF ZUK,
          MODUL(3) VALUE '001',
          VTWEG LIKE VBAK-VTWEG,
          SPART LIKE VBAK-SPART,
          WERKS LIKE LIPS-WERKS,
        END OF ZUK.
  ZUK-SPART = VBAK-SPART.
  ZUK-VTWEG = VBAK-VTWEG.
  ZUK-WERKS = LIPS-WERKS. ‘新增工廠控制
  VBRK-ZUKRI = ZUK.
ENDFORM.
如果新增一行對工廠的限制(ZUK-WERKS = LIPS-WERKS),那麼不同的工廠也要開出不同的發票。
2、阻止拆分
爲了阻止拆分,可以將影響拆分的字段設置成相同的內容。例如,兩個含有不同裝運條件的交貨單將被拆分開票,因爲裝運條件被拷貝到了發票擡頭(VBRK)中。如果這個字段被清空了,那麼拆分將不再發生。示例例程如下:
FORM DATEN_KOPIEREN_900.
  DATA: BEGIN OF ZUK,
          MODUL(3) VALUE '001',
          VTWEG LIKE VBAK-VTWEG,
          SPART LIKE VBAK-SPART,
        END OF ZUK.
  ZUK-SPART = VBAK-SPART.
  ZUK-VTWEG = VBAK-VTWEG.
  VBRK-ZUKRI = ZUK.
  CLEAR VBRK-VSBED. ‘ 清空裝運條件字段
ENDFORM.
新增一行:Clear VBRK-VSBED清空發票擡頭(內存臨時數據)的裝運條件字段。激活後分配給到發票的複製控制,開票時,裝運條件不再作爲拆分依據了。
3、用自定義字段合併、拆分
如果客戶每天發生多筆業務,那麼每天就會有很多發貨過賬和發票。有些客戶希望同一天同一個工廠開具一張發票,另一些客戶希望爲每一筆到達貨物出具發票。
爲滿足上述需求,我們將創建一個自定義的數據傳輸例程,此例程基於一個自定義的客戶數據字段(例如KNA1-KATR7)來決定交貨單是否應該被合併或拆分到發票。爲了實現它,訂單號碼將被添加到VBRK-ZUKRI字段中。
(1)本例中將使用KNA1-KATR7,將其描述更改爲“Consolidated Invoice”。
(2)在SM30中,定義KATR7只允許有2個值:“”代表拆分、“X”代表合併。
(3)使KATR7字段在數據傳輸例程中生效。將KATR7添加到結構KUAGV中的字結構KUAGVZ中,激活。
(4)在數據傳輸例程中添加如下代碼:
FORM DATEN_KOPIEREN_900.
  DATA: BEGIN OF ZUK,
          MODUL(3) VALUE '001',
          VTWEG LIKE VBAK-VTWEG,
          SPART LIKE VBAK-SPART,
          WERKS LIKE LIPS-WERKS,
          VBELN LIKE VBAK-VBELN,
        END OF ZUK.
  ZUK-SPART = VBAK-SPART.
  ZUK-VTWEG = VBAK-VTWEG.
  ZUK-WERKS = LIPS-WERKS. ‘新增工廠控制
  IF VBAK-KATR7 IS INITIAL.
    ZUK-VBELN = VBAK-VBELN.
  ENDIF.
  VBRK-ZUKRI = ZUK.
ENDFORM.
如果在客戶數據中維護了客戶組7(KNA1-KATR7)爲“X”,將集中出具發票。

延伸閱讀、相關的OSS ID:
11162 Invoice Split Criteria in Billing Documents
36832 Invoice Split Fields from the Sales Order
308733 Billing Split Due to the Person Number
Symptom
The Chinese tax regulations require that any invoice issued by the Chinese companies complies with the following rules:
  • The invoice can not have more than N line items. The invoice has to be printed on a pre-printed paper and the area for describing the invoiced line items is limited by the paper template used for that. The limit for the number of line items in an invoice is varying for different provinces.
  • The invoice amount should not exceed a certain max. amount. This amount is different for different provinces too, but depends also on the type of the issuing company, as well as on the type of the issued document. Further information about the applicable limits for each case you can obtain at the local tax bureau.
Solution
DATA: BEGIN OF ZUK906,
          MODUL(3) VALUE '906',
          VTWEG  LIKE VBAK-VTWEG,
          SPART  LIKE VBAK-SPART,
          VGBEL  LIKE VBRP-VGBEL,
          BILLNO LIKE TVKO-MAXBI,
        END OF ZUK906.
  DATA: BEGIN OF SIZE_SPLIT OCCURS 0,
          KUNRG         LIKE VBRK-KUNRG,
          KUNAG         LIKE VBRK-KUNAG,
          ZTERM         LIKE VBRK-ZTERM.
          INCLUDE STRUCTURE ZUK906.
  DATA:   ITEMNO        LIKE TVKO-MAXBI,
          AMOUNT_LEFT   LIKE VBRK-NETWR.
  DATA: END OF SIZE_SPLIT.
  DATA: LAST_BILL_NO LIKE TVKO-MAXBI VALUE 0.
  FORM DATEN_KOPIEREN_906.
  DATA: MATCH_INDEX LIKE SY-TABIX value 0,
        lv_Split_Amount like vbrk-netwr value 0.
* Determine split amount - any specific determination should be
* entered here! Assign value 0 in case no split is necessary.
* IMPORTANT: Split amount value should be the net amount of the billing
* document!!! Please reserve the necessary amount for taxes!!! Example:
*   if VBRK-WAERK = 'CNY'.
*     case VBRK-BUKRS.
*       when 'CN10'. LV_SPLIT_AMOUNT = 10000000000.
*       when 'CN11'. LV_SPLIT_AMOUNT = 50000000.
*       when others. LV_SPLIT_AMOUNT = 0.
*     endcase.
*   else.
*     LV_SPLIT_AMOUNT = 0.
*   endif.
    LV_SPLIT_AMOUNT = 200000000.
* Check if appropriate split part already exists
    LOOP AT SIZE_SPLIT WHERE
         KUNRG = VBRK-KUNRG AND
         KUNAG = VBRK-KUNAG AND
         ZTERM = VBRK-ZTERM AND
         VGBEL = VBRP-VGBEL.
* Check for additional split criteria:
* max amount and max.no. of items in billing document
      IF ( lv_Split_Amount is initial or    "No split due to max.amount
           size_split-Amount_Left >= VBRP-NETWR ) "or limit not reached
         AND
         ( TVKO-MAXBI IS INITIAL OR         "No split due to max.numbe
            SIZE_SPLIT-ITEMNO < TVKO-MAXBI ). "or limit not reached
         MATCH_INDEX = SY-TABIX. EXIT.
      ENDIF.
    ENDLOOP.
* Appropriate entry not found - load values for a new SIZE_SPLIT recor
    IF MATCH_INDEX = 0.
      IF LV_SPLIT_AMOUNT > 0 and VBRP-NETWR > LV_SPLIT_AMOUNT.
        mesSage E220(F5A).
      ENDIF.
      CLEAR SIZE_SPLIT.
      MOVE-CORRESPONDING VBRK TO SIZE_SPLIT.
      MOVE-CORRESPONDING VBRP TO SIZE_SPLIT.
      ADD 1 TO LAST_BILL_NO.
      SIZE_SPLIT-BILLNO      = LAST_BILL_NO.
      SIZE_SPLIT-AMOUNT_LEFT = LV_SPLIT_AMOUNT.
    ENDIF.
    IF NOT TVKO-MAXBI IS INITIAL.
      ADD 1 TO SIZE_SPLIT-ITEMNO.
    ENDIF.
    IF NOT lv_Split_Amount IS INITIAL.
      SUBTRACT VBRP-NETWR FROM SIZE_SPLIT-AMOUNT_LEFT.
    ENDIF.
*   Store actual billing document counter and item counter
    IF MATCH_INDEX = 0.
      APPEND SIZE_SPLIT.
    ELSE.
      MODIFY SIZE_SPLIT INDEX MATCH_INDEX.
    ENDIF.
    ZUK906-BILLNO = SIZE_SPLIT-BILLNO.
* End of billing document split by number of allowed items
    ZUK906-VTWEG = VBAK-VTWEG.
    ZUK906-SPART = VBRP-SPART.
    IF KURGV-PERFK = SPACE.
      ZUK906-VGBEL = VBRP-VGBEL.
    ENDIF.
    VBRK-ZUKRI = ZUK906.
ENDFORM.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章