Skip to main content

Celonis Product Documentation

Prerequisites for the case-centric Customer Consignment Stock app

Before you install the case-centric version of the Customer Consignment Stock app, you’ll need to complete these prerequisite steps:

  1. Install and configure a Data Connection between Celonis and your source system.

    If you’ve already set up a Data Connection to this source system, you can reuse it. If not, you'll need to install the Celonis components in your source system, set up the extractor server, and configure a Data Connection in the Celonis platform. Data Connections has the instructions to do this.Data Connections

  2. Set up extraction jobs to extract the required tables from SAP ECC. The reference list is in Required tables and columns for the case-centric Customer Consignment Stock app.

  3. Create a transformation for each of the tables for the Data Model as follows:

    1. Go to Data Integration and select the Data Jobs area.

    2. Click Add Data Job to create a new data job.

    3. Add each of the transformations that we’ve provided in Data Model tables for the case-centric Customer Consignment Stock app.

    4. Run the data job to create the tables.

  4. Create a new Data Model using the tables, as described in Data Model for the case-centric Customer Consignment Stock app.

  5. Load the Data Model with your data.

When the Data Connection is in place and you’ve extracted the tables, created the transformations, and created and loaded the Data Model, follow the steps in Installing the case-centric Customer Consignment Stock app.

If you need to get help with any of the app’s prerequisites or setup steps, Support tells you how.

Required tables and columns for the case-centric Customer Consignment Stock app

Here’s the reference list of the SAP ECC tables that you need to extract data from for the Customer Consignment Stock app:

MARC, MARA, MAKT, MARM, MBEW, MSEG, MKPF, MSKU, MSKUH, T001, T001W, T001K, T003T, T006, T006D, TCURR, TCURF, TCURX, T156, T156T, TVAKT, TVKOT, TVAPT, TVAGT, USR02, KNA1, VBAP, VBAK, VBEP, VBAK, VBRP

Data Model tables for the case-centric Customer Consignment Stock app

Add the transformations in this section to your data job to create each of the tables for the Data Model. In the data job, click Add Transformation, give it a suitable name, and paste in one of the transformations. Each transformation creates one of the tables or a helper table associated with it.

Here’s the full list of Data Model tables to create:

When you assemble the Data Model, you'll need to create aliases for the IM_MARC, CS_VBAK, CS_VBAP, CS_VBEP, and CS_VBRP tables to rename them back to the standard SAP ECC table names.

IM_MARC (material master data) table

The SAP ECC tables that supply data for these transformations are MARC, MARA, MAKT, MARM, T001W, T001K, T001, T006, T006D, MBEW, TCURR, TCURF, and TCURX.

The three transformations here create the MARC table and two helper tables for currency conversion and quantity conversion. You can put them all under the same transformation or create three separate transformations.

  • If your data pool already contains the table IM_MARC from the standard Celonis data model for Inventory Management, you can use that table in place of the MARC table.

  • If your data pool already contains the tables TCURF_CC and TCURR_CC from the data model for another app, you can use those in place of the currency conversion table.

Transformation 1: currency conversion table
-- Query No: 1
DROP TABLE IF EXISTS "TCURF_CC";
-- Query No: 2
DROP TABLE IF EXISTS "TCURF_TMP";
-- Query No: 3
CREATE TABLE "TCURF_TMP" AS ( 
   SELECT
   *
 ,CAST(NULL AS TIMESTAMP) AS "VALID_START"
 ,CAST(NULL AS TIMESTAMP) AS "VALID_END"
 ,DENSE_RANK() OVER (ORDER BY
   TCURF."MANDT"
   ,TCURF."KURST"
   ,TCURF."FCURR"
   ,TCURF."TCURR"
   ) AS "TCURF_KEY"
 ,ROW_NUMBER() OVER (PARTITION BY
   TCURF."MANDT"
   ,TCURF."KURST"
   ,TCURF."FCURR"
   ,TCURF."TCURR"
   ORDER BY (99999999-CAST(TCURF."GDATU" AS INT)) ASC) AS "TCURF_ROWNR"
FROM
 "TCURF" as TCURF
WHERE 1=1
 AND (99999999-cast(TCURF."GDATU" as int)) >= 18000000
   AND (99999999-cast(TCURF."GDATU" as int)) <= 20990000);

SELECT ANALYZE_STATISTICS('TCURF_TMP');

-- Query No: 4
CREATE TABLE "TCURF_CC" AS (
SELECT
 *
FROM
 "TCURF_TMP");

SELECT ANALYZE_STATISTICS('TCURF_CC');

-- Query No: 5
UPDATE
 "TCURF_CC"
SET
 "VALID_START" = CAST(LPAD(CAST((99999999-CAST("GDATU" AS INT)) AS VARCHAR(10)),8,'0') || ' ' || '00:00:00' AS TIMESTAMP);
-- Query No: 6
UPDATE
 "TCURF_CC" AS TCURF_CC
SET
 "VALID_END" = CAST(LPAD(CAST((99999999-CAST(TCURF_TMP."GDATU" AS INT)) AS VARCHAR(10)),8,'0') || ' ' || '00:00:00' AS TIMESTAMP)
FROM
 "TCURF_TMP" AS TCURF_TMP
WHERE 1=1
 AND TCURF_CC."TCURF_KEY" = TCURF_TMP."TCURF_KEY"
 AND TCURF_CC."TCURF_ROWNR" + 1 = TCURF_TMP."TCURF_ROWNR";
-- Query No: 7
UPDATE
 "TCURF_CC"
SET
 "VALID_END" = '2099-01-01'
WHERE
 "VALID_END" IS NULL;
-- Query No: 8
DROP TABLE IF EXISTS "TCURF_TMP";

--Add statistics for created temporary join table
SELECT ANALYZE_STATISTICS('TCURF_CC');
-------------------------------------
-------------------------------------
-- Statement: Database Preparation: Create Table: TCURR_CC

-- Query No: 1
DROP TABLE IF EXISTS "TCURR_CC";
-- Query No: 2
DROP TABLE IF EXISTS "TCURR_TMP";


-- Query No: 3
CREATE TABLE "TCURR_TMP" AS (                           
SELECT
   TCURR."MANDT"
   ,TCURR."KURST"
   ,TCURR."FCURR"
   ,TCURR."TCURR"
   ,CAST (TCURR."GDATU" AS VARCHAR) AS GDATU
   ,TCURR."UKURS"
   ,CAST (TCURR."FFACT" AS FLOAT) AS FFACT
   ,CAST (TCURR."TFACT" AS FLOAT) AS TFACT
 ,CAST(NULL AS TIMESTAMP) AS "VALID_START"
 ,CAST(NULL AS TIMESTAMP) AS "VALID_END"
 ,DENSE_RANK() OVER (ORDER BY
   TCURR."MANDT"
   ,TCURR."KURST"
   ,TCURR."FCURR"
   ,TCURR."TCURR"
   ) AS "TCURR_KEY"
 ,ROW_NUMBER() OVER (PARTITION BY
   TCURR."MANDT"
   ,TCURR."KURST"
   ,TCURR."FCURR"
   ,TCURR."TCURR"
   ORDER BY (99999999-CAST(TCURR."GDATU" AS INT)) ASC) AS "TCURR_ROWNR" 
FROM
 "TCURR" AS TCURR
WHERE 1=1
 AND (99999999-CAST(TCURR."GDATU" AS INT)) >= 18000000
   AND (99999999-CAST(TCURR."GDATU" AS INT)) <= 20990000
);

SELECT ANALYZE_STATISTICS('TCURR_TMP');

-- Query No: 4
CREATE TABLE "TCURR_CC" AS (
SELECT
 *
FROM
 "TCURR_TMP"
);
-- Query No: 5
UPDATE
 "TCURR_CC" AS TCURR_CC
SET
 "VALID_START" = CAST(LPAD(CAST((99999999-cast(TCURR_CC."GDATU" AS INT)) AS VARCHAR(10)),8,'0') || ' ' || '00:00:00' AS TIMESTAMP)
;
-- Query No: 6
UPDATE
 "TCURR_CC"  AS TCURR_CC
SET
 "VALID_END" = CAST(LPAD(CAST((99999999-CAST(TCURR_TMP."GDATU" AS INT)) AS VARCHAR(10)),8,'0') || ' ' || '00:00:00' AS timestamp)
FROM
 "TCURR_TMP" AS TCURR_TMP
WHERE 1=1
 AND TCURR_CC."TCURR_KEY" = TCURR_TMP."TCURR_KEY" 
 AND TCURR_CC."TCURR_ROWNR" + 1=TCURR_TMP."TCURR_ROWNR"
;
-- Query No: 7
UPDATE
 "TCURR_CC" AS TCURR_CC
SET
 "VALID_END" = '2099-01-01'
WHERE
 "VALID_END" IS NULL
;
--Add statistics for created temporary join table
SELECT ANALYZE_STATISTICS('TCURR_CC');
-- Query No: 8
DROP TABLE IF EXISTS "TCURR_TMP";
Transformation 2: quantity conversion table
DROP TABLE IF EXISTS QTY_UNIT_CONVERSION;

CREATE TABLE QTY_UNIT_CONVERSION AS
   SELECT
       "MARA"."MANDT"
       ,"MARA"."MATNR"
       ,"MARA"."MEINS" AS "FROM_UNIT"
       ,CASE
           WHEN "T006"."DIMID" != 'AAAADL' THEN "T006D"."MSSIE" -- check if the dimension (DIMID) is an SI unit (ie. != 'AAAADL'), if it is then take the SI base unit (MSSIE)
           WHEN "T006"."DIMID" = 'AAAADL' THEN "MARM"."MEINH" -- check if the dimension (DIMID) is not an SI unit (ie. = AAAADL), if not then take the non-SI base unit (MEINH)
       END AS "TO_UNIT"
       ,CASE
           WHEN "T006"."DIMID" != 'AAAADL' THEN "T006"."ZAEHL" / "T006"."NENNR" * POWER(10, "T006"."EXP10") -- if DIMID is an SI unit, apply the SI conversion logic to convert to base unit. (eg. converting Grams into KG)
           WHEN "T006"."DIMID" = 'AAAADL' THEN "MARM"."UMREN"/ "MARM"."UMREZ" -- if DIMID is not an SI unit, apply the non-SI conversion logic to convert to base unit (eg. converting Pallets into Pieces)
       END AS "CONVERSION_FROM_TO"
       ,"T006"."DIMID" AS "CONVERSION_DIMENSION"
   FROM "MARA"
   LEFT JOIN T006 AS T006 ON 1=1
       AND T006.MANDT = MARA.MANDT
       AND T006.MSEHI = MARA.MEINS
   LEFT JOIN T006D ON 1=1
       AND T006D.MANDT = T006.MANDT
       AND T006D.DIMID = T006.DIMID
   LEFT JOIN "MARM" ON 1=1
       AND "MARA"."MANDT" = "MARM"."MANDT" -- TB: MOVED UP
       AND "MARA"."MATNR" = "MARM"."MATNR"
       AND "MARM"."MEINH" = 'EA' -- target/base unit for all non SI units. If you have more than 1 base non-SI unit, please create a Service Desk ticket to discuss your use case with the Supply Chain Team.
;
--Add statistics for created temporary join table
SELECT ANALYZE_STATISTICS('QTY_UNIT_CONVERSION');
Transformation 3: IM_MARC - material table
DROP TABLE IF EXISTS IM_MARC;

CREATE TABLE IM_MARC AS (
 SELECT
   MARC.*
   ,T001W.REGIO
   ,T001W.COUNC
   ,T001W.LAND1
   ,T001W.CITYC
   ,T001W.ORT01
   ,T001W.NAME1
   ,MAKT.MAKTX
   ,T001K.BUKRS -- Company Code
   ,T001.BUTXT -- Company Name
   ,CASE       -- 01
       WHEN MBEW.VPRSV = 'V' THEN COALESCE(MBEW.VERPR / NULLIFZERO(MBEW.PEINH), MBEW.VERPR)
       WHEN MBEW.VPRSV = 'S' THEN COALESCE(MBEW.STPRS / NULLIFZERO(MBEW.PEINH), MBEW.STPRS)
       ELSE MBEW.SALK3 / MBEW.LBKUM
    END AS "VERPR"  -- Dividing the MAP by the price unit.
   ,(CASE
       WHEN MBEW.VPRSV = 'V'
       THEN 
           CASE
               WHEN "T001"."WAERS" = '<%=currency%>' THEN MBEW."VERPR"*COALESCE("TCURX"."TDEC",1)
               WHEN "TCURR_CC"."UKURS" < 0 THEN (MBEW."VERPR"*COALESCE("TCURX"."TDEC",1))*(1/ABS(TCURR_CC.UKURS)/(CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END))*(CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
               WHEN "TCURR_CC"."UKURS" > 0 THEN (MBEW."VERPR"*COALESCE("TCURX"."TDEC",1))*(TCURR_CC.UKURS/CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END*CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END) 
           END
       WHEN MBEW.VPRSV = 'S'
       THEN
           CASE
               WHEN "T001"."WAERS" = '<%=currency%>' THEN MBEW."STPRS"*COALESCE("TCURX"."TDEC",1)
               WHEN "TCURR_CC"."UKURS" < 0 THEN (MBEW."STPRS"*COALESCE("TCURX"."TDEC",1))*(1/ABS(TCURR_CC.UKURS)/(CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END))*(CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
               WHEN "TCURR_CC"."UKURS" > 0 THEN (MBEW."STPRS"*COALESCE("TCURX"."TDEC",1))*(TCURR_CC.UKURS/CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END*CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
           END 
       ELSE
           CASE
               WHEN "T001"."WAERS" = '<%=currency%>' THEN MBEW.SALK3 / MBEW.LBKUM *COALESCE("TCURX"."TDEC",1)
               WHEN "TCURR_CC"."UKURS" < 0 THEN (MBEW.SALK3 / MBEW.LBKUM *COALESCE("TCURX"."TDEC",1))*(1/ABS(TCURR_CC.UKURS)/(CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END))*(CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
               WHEN "TCURR_CC"."UKURS" > 0 THEN (MBEW.SALK3 / MBEW.LBKUM *COALESCE("TCURX"."TDEC",1))*(TCURR_CC.UKURS/CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END*CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
           END
   END) / NULLIFZERO(MBEW.PEINH) / "QUC"."CONVERSION_FROM_TO"  AS "VERPR_CON"   
   ,"MARA"."MEINS"
   ,"QUC"."TO_UNIT" AS "MSSIE"
   FROM MARC
   LEFT JOIN T001W ON 1=1
       AND MARC.MANDT = T001W.MANDT
       AND MARC.WERKS = T001W.WERKS
   LEFT JOIN MAKT ON 1=1
       AND MARC.MANDT = MAKT.MANDT
       AND MARC.MATNR = MAKT.MATNR
       AND MAKT.SPRAS = 'E'
   LEFT JOIN T001K ON 1=1
       AND MARC.MANDT = T001K.MANDT
       AND MARC.WERKS = T001K.BWKEY 
   LEFT JOIN T001 ON 1=1
       AND T001.MANDT = T001K.MANDT
       AND T001.BUKRS = T001K.BUKRS
   LEFT JOIN MBEW ON 1=1
       AND MBEW.MANDT = MARC.MANDT
       AND MBEW.MATNR = MARC.MATNR
       AND MBEW.BWKEY = MARC.WERKS
       AND MBEW.BWTAR IS NULL -- removes duplicate rows in this table.
   LEFT JOIN "MARA" AS "MARA" ON 1=1
       AND "MARA"."MANDT" = "MARC"."MANDT"
       AND "MARA"."MATNR" = "MARC"."MATNR"
   -- Qty Conversion
   LEFT JOIN "QTY_UNIT_CONVERSION" AS "QUC" ON 1=1
       AND "QUC"."MANDT" = "MARA"."MANDT"
       AND "QUC"."MATNR" = "MARA"."MATNR"
       AND "QUC"."FROM_UNIT" = "MARA"."MEINS"
   -- Currency Conversion for MAP
   LEFT JOIN "TCURR_CC" AS "TCURR_CC" ON 1=1
       AND "TCURR_CC"."MANDT" = T001.MANDT
       AND "TCURR_CC"."TCURR" = '<%=currency%>'
       AND "TCURR_CC"."FCURR" = "T001"."WAERS"
       AND "TCURR_CC"."KURST" = 'M'
       AND CURRENT_DATE >= "TCURR_CC"."VALID_START"
       AND CURRENT_DATE < "TCURR_CC"."VALID_END"
   LEFT JOIN "TCURF_CC" AS "TCURF_CC" ON 1=1
       AND "TCURR_CC"."MANDT" = "TCURF_CC"."MANDT"
       AND "TCURR_CC"."TCURR" = "TCURF_CC"."TCURR"
       AND "TCURR_CC"."FCURR" = "TCURF_CC"."FCURR"
       AND "TCURR_CC"."KURST" = "TCURF_CC"."KURST"
       AND CURRENT_DATE >= "TCURF_CC"."VALID_START"
       AND CURRENT_DATE < "TCURF_CC"."VALID_END"
   LEFT JOIN (
       SELECT
           "TCURX"."CURRKEY"
           ,CAST("TCURX"."CURRDEC" AS INT) AS "CURRDEC"
           ,POWER(CAST(10 AS FLOAT),(2-"CURRDEC")) AS "TDEC"
       FROM "TCURX"
   ) AS "TCURX" ON 1=1
       AND "T001"."WAERS" = "TCURX"."CURRKEY"
);

--Add statistics for created table
SELECT ANALYZE_STATISTICS('IM_MARC');
CONSIGNMENT_MOVEMENTS table

The SAP ECC tables that supply data for this transformation are MSEG, MKPF, T156, T156T, T001, T003T, and USR02.

Transformation 4: CONSIGNMENT_MOVEMENTS table
DROP TABLE IF EXISTS CONSIGNMENT_MOVEMENTS;

CREATE TABLE CONSIGNMENT_MOVEMENTS AS(
SELECT
 CAST(MKPF.CPUDT AS DATE) + COALESCE(CAST(MKPF.CPUTM AS TIME),CAST('00:00:00' AS TIME)) AS EVENTTIME
 ,YEAR(CAST(MKPF.CPUDT AS DATE) + COALESCE(CAST(MKPF.CPUTM AS TIME),CAST('00:00:00' AS TIME))) AS JAHR
 ,MONTH(CAST(MKPF.CPUDT AS DATE) + COALESCE(CAST(MKPF.CPUTM AS TIME),CAST('00:00:00' AS TIME))) AS MONAT
,CAST(MKPF.BUDAT AS DATE) + CAST('00:00:00' AS TIME) AS BUDAT
   ,MSEG.KUNNR
   ,MSEG.VFDAT
   ,MSEG.MANDT
   ,MSEG.MBLNR
   ,MSEG.MJAHR
   ,MSEG.ZEILE
 ,T156T_DE.BTEXT AS ACTIVITY_DE
 ,T156T_EN.BTEXT AS ACTIVITY_EN
 ,T003T_DE.LTEXT AS ACTIVITY_DETAIL_DE
 ,T003T_EN.LTEXT AS ACTIVITY_DETAIL_EN
 ,MKPF.USNAM AS USER_NAME
 ,USR02.USTYP AS USER_TYPE
-- The table 'T156' lists all movement types, it can be restricted to consumption (SHKZG = 'H') or replenishment (SHKZG = 'S').
-- This first indicator is refined by using KZVBU (consumption posting - i.e. were goods actually moved) XSTBW (is the current movement type a cancellation ('X') or not (NULL))
 , CASE
       WHEN "MSEG"."SHKZG" = 'H' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" IS NULL THEN "MSEG"."MENGE"
       WHEN "MSEG"."SHKZG" = 'S' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" = 'X' THEN -1.0 * "MSEG"."MENGE"
   ELSE 0.0
 END AS CONS_QTY
 , CASE
   WHEN "MSEG"."SHKZG" = 'H' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" IS NULL THEN "MSEG"."MENGE" * COALESCE("QUC"."CONVERSION_FROM_TO",1)
       WHEN "MSEG"."SHKZG" = 'S' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" = 'X' THEN -1.0 * "MSEG"."MENGE" * COALESCE("QUC"."CONVERSION_FROM_TO",1)
   ELSE 0.0
 END AS CONS_QTY_CON
 , CASE
       WHEN "MSEG"."SHKZG" = 'S' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" IS NULL THEN "MSEG"."MENGE"
       WHEN "MSEG"."SHKZG" = 'H' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" = 'X' THEN -1.0 * "MSEG"."MENGE"
   ELSE 0.0
 END AS REPL_QTY
 , CASE
   WHEN "MSEG"."SHKZG" = 'S' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" IS NULL THEN "MSEG"."MENGE" * COALESCE("QUC"."CONVERSION_FROM_TO",1)
       WHEN "MSEG"."SHKZG" = 'H' AND "T156"."SHKZG" IS NOT NULL AND "T156"."XSTBW" = 'X' THEN -1.0 * "MSEG"."MENGE" * COALESCE("QUC"."CONVERSION_FROM_TO",1)
   ELSE 0.0
 END AS "REPL_QTY_CON"
   ,"MSEG"."MEINS"
   ,"QUC"."TO_UNIT" AS "MSSIE"
   ,"MSEG"."BWART"
   ,"MSEG"."MATNR"
   ,"MSEG"."WERKS"
   ,"MSEG"."LGORT"
   ,"MSEG"."CHARG"
   ,"MSEG"."LIFNR"
   ,"MSEG"."KDAUF"
   ,"MSEG"."KDPOS"
   ,"MSEG"."SHKZG"
   ,"MSEG"."DMBTR"
   ,"MSEG"."BWTAR"
   ,"MSEG"."MENGE"
   ,MENGE * "QUC"."CONVERSION_FROM_TO" AS MENGE_CONVERTED
   ,"MSEG"."EBELN"
   ,"MSEG"."EBELP"
   ,"MSEG"."SGTXT"
   ,"MSEG"."BUKRS"
   ,"MSEG"."RSNUM"
   ,"MSEG"."RSPOS"
   ,"MSEG"."UMWRK"
   ,"MSEG"."UMLGO"
   ,"MSEG"."LBKUM"
   ,LBKUM * "QUC"."CONVERSION_FROM_TO" AS LBKUM_CONVERTED
   ,"MSEG"."SALK3"
   ,"T001"."WAERS" -- local currency
   ,"MSEG"."PROJN"
   ,"MSEG"."AUFNR"
   ,"MSEG"."AUFPS"
   ,"MSEG"."SOBKZ"
   ,"MSEG"."KZBEW"
   ,"MSEG"."KZZUG"
   ,"MSEG"."KZVBR"
   ,"QUC"."TO_UNIT" AS MEINS_CONVERTED_TO
   ,"QUC"."CONVERSION_DIMENSION" AS CONVERSION_DIMENSION
FROM MSEG
   JOIN IM_MARC AS MARC ON 1=1
       AND MSEG.MANDT = MARC.MANDT
       AND MSEG.MATNR = MARC.MATNR
       AND MSEG.WERKS = MARC.WERKS
   JOIN MKPF AS MKPF ON 1=1
   AND MKPF.MANDT = MSEG.MANDT
   AND MKPF.MBLNR = MSEG.MBLNR
   AND MKPF.MJAHR = MSEG.MJAHR
--Movement Types Clear Names
 LEFT JOIN T156 ON 1=1 -- Consumption / Replenishment Information
   AND MSEG.MANDT = T156.MANDT
   AND MSEG.BWART = T156.BWART
    AND T156.KZVBU IS NOT NULL
 LEFT JOIN T156T AS T156T_EN ON 1=1
   AND T156T_EN.MANDT = MSEG.MANDT
   AND T156T_EN.BWART = MSEG.BWART
   AND COALESCE(MSEG.SOBKZ,'') = COALESCE(T156T_EN.SOBKZ,'')
   AND COALESCE(MSEG.KZBEW,'') = COALESCE(T156T_EN.KZBEW,'')
   AND COALESCE(MSEG.KZZUG,'') = COALESCE(T156T_EN.KZZUG,'')
   AND COALESCE(MSEG.KZVBR,'') = COALESCE(T156T_EN.KZVBR,'')
   AND T156T_EN.SPRAS = 'E'
 LEFT JOIN T156T AS T156T_DE ON 1=1
   AND T156T_DE.MANDT = MSEG.MANDT
   AND T156T_DE.BWART = MSEG.BWART
   AND COALESCE(MSEG.SOBKZ,'') = COALESCE(T156T_DE.SOBKZ,'')
   AND COALESCE(MSEG.KZBEW,'') = COALESCE(T156T_DE.KZBEW,'')
   AND COALESCE(MSEG.KZZUG,'') = COALESCE(T156T_DE.KZZUG,'')
   AND COALESCE(MSEG.KZVBR,'') = COALESCE(T156T_DE.KZVBR,'')
   AND T156T_DE.SPRAS = 'D'
 LEFT JOIN T003T AS T003T_EN ON 1=1
   AND T003T_EN.MANDT = MKPF.MANDT
   AND T003T_EN.BLART = MKPF.BLART
   AND T003T_EN.SPRAS = 'E'
 LEFT JOIN T003T AS T003T_DE ON 1=1
   AND T003T_DE.MANDT = MKPF.MANDT
   AND T003T_DE.BLART = MKPF.BLART
   AND T003T_DE.SPRAS = 'D'
--User Types
 LEFT JOIN USR02 AS USR02 ON 1=1
   AND USR02.MANDT = MKPF.MANDT
   AND USR02.BNAME = MKPF.USNAM
--Local Currency
   LEFT JOIN "T001" ON 1=1
       AND "T001"."MANDT" = "MSEG"."MANDT"
       AND "T001"."BUKRS" = "MSEG"."BUKRS"
-- Quantitiy Conversion
   LEFT JOIN "QTY_UNIT_CONVERSION" AS "QUC" ON 1=1
       AND QUC.MANDT = MARC.MANDT
       AND QUC.MATNR = MARC.MATNR
       AND QUC.FROM_UNIT = MSEG.MEINS
WHERE MSEG.SOBKZ IN ('W') AND
YEAR(CAST(MKPF.CPUDT AS DATE) + COALESCE(CAST(MKPF.CPUTM AS TIME),CAST('00:00:00' AS TIME))) > 2021
);

SELECT ANALYZE_STATISTICS('CONSIGNMENT_MOVEMENTS');
CONSIGNMENT_STOCK_HISTORY table

The SAP ECC tables that supply data for this transformation are MSKU and MSKUH.

Transformation 5: CONSIGNMENT_STOCK_HISTORY table
-----------------------------------------------------------------------------
-- Step 1: Create a table that unions MSKU and MSKUH
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
--- Step 1a: Create a temporary view for MSKUH, , using the lead function to get the next fiscal year entry per material || plant || eventtime combination.
-----------------------------------------------------------------------------

DROP TABLE  IF EXISTS MSKUH_TMP;

CREATE TABLE MSKUH_TMP AS (
   SELECT 
       MANDT
       ,MATNR
       ,WERKS
       ,SOBKZ
       ,KUNNR
       ,ADD_MONTHS(
           CAST(
               LAST_DAY(
                   CAST(
                       LFGJA ||'-'||LFMON||'-'||'01' AS DATE
                   )
               )
               +
               CAST(
                   '23:59:59' AS TIME
               )
               AS DATETIME
           )
           , 0
       ) AS 'CALENDAR_DATE'
       ,LEAD(LFGJA,1,0) OVER (PARTITION BY MANDT, MATNR, WERKS, SOBKZ, KUNNR ORDER BY LFGJA, LFMON ASC) AS NEXT_ROW_FISCAL_YEAR -- The next fiscal year in the table is returned to each material || plant combination. Eg. If material A has an entry in 01.2020 and another 02.2021, 2021 would be returned as Next_row_fiscal_year for 01.2020, and 0 would be returned for the row containing 02.2021 (as there is no leading fiscal year)
       ,SUM(KULAB) AS KULAB -- Amount of unrestricted, available stock
       ,SUM(KUINS) AS KUINS -- Amount of stock in quality inspection
       ,SUM(KUEIN) AS KUEIN -- Amount of stock in transfer
       ,SUM(KUUML) AS KUUML -- Amount of blocked stock
   FROM MSKUH
   WHERE
       EXISTS(
           SELECT *
           FROM IM_MARC AS MARC
           WHERE 1=1
               AND MSKUH.MANDT = MARC.MANDT
               AND MSKUH.MATNR = MARC.MATNR
               AND MSKUH.WERKS = MARC.WERKS
           )
       AND (KULAB IS NOT NULL
       OR KUINS IS NOT NULL
       OR KUEIN IS NOT NULL
       OR KUUML IS NOT NULL)
      
   GROUP BY MANDT, MATNR, WERKS, SOBKZ, KUNNR, CALENDAR_DATE, LFGJA, LFMON
   ORDER BY CALENDAR_DATE ASC
);

SELECT ANALYZE_STATISTICS('MSKUH_TMP');

-----------------------------------------------------------------------------
--- Step 1b: Create MSKU+MSKUH merged table by unioning.
-----------------------------------------------------------------------------

DROP TABLE IF EXISTS MSKU_FULL_TMP;

CREATE TABLE MSKU_FULL_TMP AS  (
   SELECT
       MANDT
       ,MATNR
       ,WERKS
       ,SOBKZ
       ,KUNNR
       ,ADD_MONTHS(
           CAST(
               LAST_DAY(
                   CAST(
                       LFGJA ||'-'||LFMON||'-'||'01' AS DATE
                   )
               )
               + CAST('23:59:59' AS TIME) AS DATETIME
           )
           , 0
       ) AS 'CALENDAR_DATE'
       ,SUM(KULAB) AS KULAB -- Amount of unrestricted, available stock
       ,SUM(KUINS) AS KUINS -- Amount of stock in quality inspection
       ,SUM(KUEIN) AS KUEIN -- Amount of stock in transfer
       ,SUM(KUUML) AS KUUML -- Amount of blocked stock
   FROM MSKU
       WHERE
           EXISTS(
               SELECT *
               FROM IM_MARC AS MARC
               WHERE 1=1
                   AND MSKU.MANDT = MARC.MANDT
                   AND MSKU.MATNR = MARC.MATNR
                   AND MSKU.WERKS = MARC.WERKS
               )
           AND (KULAB IS NOT NULL
           OR KUINS IS NOT NULL
           OR KUEIN IS NOT NULL
           OR KUUML IS NOT NULL
           )
       GROUP BY
           MANDT, MATNR, WERKS, SOBKZ, KUNNR, CALENDAR_DATE

   UNION ALL

   SELECT 
       MANDT
       ,MATNR
       ,WERKS
       ,SOBKZ
       ,KUNNR
       ,CALENDAR_DATE
       ,KULAB -- Amount of unrestricted, available stock
       ,KUINS -- Amount of stock in quality inspection
       ,KUEIN -- Amount of stock in transfer
       ,KUUML -- Amount of blocked stock
   FROM MSKUH_TMP
   WHERE NEXT_ROW_FISCAL_YEAR >= 2000 OR NEXT_ROW_FISCAL_YEAR = 0 -- ensures that only relevant fiscal years are used in creating the valuated stock history table. In the case of 0, that's the most recent entry with no lead fiscal year.
   ORDER BY CALENDAR_DATE DESC
);

SELECT ANALYZE_STATISTICS('MSKU_FULL_TMP');

-----------------------------------------------------------------------------
-- Step 2: Create a Calendar Table that can be joined to
-----------------------------------------------------------------------------


-----------------------------------------------------------------------------
-- Step 2a: Create a table with two entries
-----------------------------------------------------------------------------

DROP TABLE IF EXISTS "TMP_CALENDAR";
CREATE TABLE TMP_CALENDAR
(
   "EVENTTIME" DATETIME PRIMARY KEY
);

INSERT INTO TMP_CALENDAR SELECT DISTINCT CAST(LAST_DAY(CAST(current_timestamp AS DATE)) + CAST('00:00:00' AS TIME) AS DATETIME);
INSERT INTO TMP_CALENDAR SELECT MIN(CALENDAR_DATE) FROM MSKU_FULL_TMP;
SELECT ANALYZE_STATISTICS('TMP_CALENDAR');

-----------------------------------------------------------------------------
-- Step 2b: Slice the table to create a date for every month between the upper dates
-----------------------------------------------------------------------------

DROP TABLE IF EXISTS "CALENDAR";

CREATE TABLE CALENDAR AS(
SELECT DISTINCT
   LAST_DAY(CAST(slice_time AS DATE)) AS 'EVENTTIME'
FROM TMP_CALENDAR C
TIMESERIES slice_time as '25 DAYS'
OVER (ORDER BY CAST(C."EVENTTIME" as DATETIME)));

SELECT ANALYZE_STATISTICS('CALENDAR');

-----------------------------------------------------------------------------
-- Step 3: Join the calendar to the MARC table, ie. give each material and plant combination n eventtimes.
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
---Step 3a: Create a single instance for each  MANDT, MATNR, WERKS, KUNNR in order to create only one calendar record per day and avoid duplicates
-----------------------------------------------------------------------------

DROP TABLE IF EXISTS MSKU_FULL_TMP_DEDUPLICATED;
CREATE TABLE MSKU_FULL_TMP_DEDUPLICATED AS
   SELECT DISTINCT
       MANDT
       ,MATNR
       ,WERKS
       ,SOBKZ
       ,KUNNR
       ,MIN(CALENDAR_DATE) AS MIN_CALENDAR_DATE
       ,SUM(KULAB) AS SUM_KULAB
       ,SUM(KUINS) AS SUM_KUINS
       ,SUM(KUEIN) AS SUM_KUEIN
       ,SUM(KUUML) AS SUM_KUUML
   FROM MSKU_FULL_TMP
   GROUP BY MANDT,MATNR, WERKS, SOBKZ, KUNNR
;

SELECT ANALYZE_STATISTICS('MSKU_FULL_TMP_DEDUPLICATED');


-----------------------------------------------------------------------------
--Step 3b: Create MARC_DATES table for each material and plant combination n eventtimes
-----------------------------------------------------------------------------

DROP TABLE IF EXISTS MARC_DATES;
CREATE TABLE MARC_DATES AS
   SELECT
       MSKU_FULL_TMP.MANDT
       ,MSKU_FULL_TMP.MATNR
       ,MSKU_FULL_TMP.WERKS
       ,MSKU_FULL_TMP.SOBKZ
       ,MSKU_FULL_TMP.KUNNR
       ,CALENDAR.EVENTTIME
   FROM "MSKU_FULL_TMP_DEDUPLICATED" AS MSKU_FULL_TMP
   JOIN CALENDAR AS CALENDAR ON 1=1
       AND  MSKU_FULL_TMP.MIN_CALENDAR_DATE <= CALENDAR.EVENTTIME -- filters the table such that the minimum eventtime for each material || plant combination equals the minimum eventtime from the MSKU + MSKUH table.
       AND (
                   MSKU_FULL_TMP.SUM_KULAB > 0 -- filters the table on material || plant combinations where unrestricted available stock has been > 0 at least once. 
                   OR MSKU_FULL_TMP.SUM_KUINS > 0 -- filters the table on material || plant combinations where stock in quality inspected has been > 0 at least once. 
                   OR MSKU_FULL_TMP.SUM_KUEIN > 0 -- filters the table on material || plant combinations where stock in transfer has been > 0 at least once. 
                   OR MSKU_FULL_TMP.SUM_KUUML > 0 -- filters the table on material || plant combinations where blocked stock has been > 0 at least once. 
               ) -- filters the table on material || plant combinations where inventory has been > 0 at least once 
;

-----------------------------------------------------------------------------
-- Step 4: Left Join the MARC Calendar table created in the last step with the MSKU + MSKUH table created in Step 1.
-- This merges the two tables, creating dates for each month of the MSKU+MSKUH table where they did not previously exist
-----------------------------------------------------------------------------

DROP TABLE IF EXISTS MSKU_FULL_TMP_1;

CREATE TABLE MSKU_FULL_TMP_1 AS(
SELECT DISTINCT
   MARC_DATES.MANDT
   ,MARC_DATES.MATNR
   ,MARC_DATES.WERKS
   ,MARC_DATES.SOBKZ
   ,MARC_DATES.KUNNR
   ,MSKU_FULL_TMP.KULAB
   ,MSKU_FULL_TMP.KUINS
   ,MSKU_FULL_TMP.KUEIN
   ,MSKU_FULL_TMP.KUUML
   ,EVENTTIME
   FROM MARC_DATES
   LEFT JOIN MSKU_FULL_TMP ON
       MARC_DATES.EVENTTIME =  MSKU_FULL_TMP.CALENDAR_DATE
       AND MARC_DATES.MATNR = MSKU_FULL_TMP.MATNR
       AND MARC_DATES.WERKS = MSKU_FULL_TMP.WERKS
       AND MARC_DATES.SOBKZ = MSKU_FULL_TMP.SOBKZ
       AND MARC_DATES.KUNNR = MSKU_FULL_TMP.KUNNR
ORDER BY EVENTTIME DESC
);

SELECT ANALYZE_STATISTICS('MSKU_FULL_TMP_1');

-----------------------------------------------------------------------------
-- Step 5: If there was no match on the primary keys in step 4, ie. no calendar date for a certain material, there will be material and plant
-- combinations that have null values for LABST. In this step we solve this by carrying the previous non-null entries to all nulls,
-- in ascending order on eventtime, until the next non-null is found. This process is completed until the most recent month.
-----------------------------------------------------------------------------
DROP TABLE IF EXISTS MSKU_FULL_TMP_2;

CREATE TABLE MSKU_FULL_TMP_2 AS(
SELECT 
   MSKU_FULL_TMP_1.MANDT
   ,MSKU_FULL_TMP_1.MATNR
   ,MSKU_FULL_TMP_1.WERKS
   ,MSKU_FULL_TMP_1.SOBKZ
   ,MSKU_FULL_TMP_1.KUNNR
   ,FIRST_VALUE(MSKU_FULL_TMP_1.KULAB IGNORE NULLS) OVER (PARTITION BY MSKU_FULL_TMP_1.MATNR, MSKU_FULL_TMP_1.WERKS, MSKU_FULL_TMP_1.SOBKZ, MSKU_FULL_TMP_1.KUNNR ORDER BY MSKU_FULL_TMP_1."EVENTTIME" DESC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS KULAB
   ,FIRST_VALUE(MSKU_FULL_TMP_1.KUINS IGNORE NULLS) OVER (PARTITION BY MSKU_FULL_TMP_1.MATNR, MSKU_FULL_TMP_1.WERKS, MSKU_FULL_TMP_1.SOBKZ, MSKU_FULL_TMP_1.KUNNR ORDER BY MSKU_FULL_TMP_1."EVENTTIME" DESC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS KUINS
   ,FIRST_VALUE(MSKU_FULL_TMP_1.KUEIN IGNORE NULLS) OVER (PARTITION BY MSKU_FULL_TMP_1.MATNR, MSKU_FULL_TMP_1.WERKS, MSKU_FULL_TMP_1.SOBKZ, MSKU_FULL_TMP_1.KUNNR ORDER BY MSKU_FULL_TMP_1."EVENTTIME" DESC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS KUEIN
   ,FIRST_VALUE(MSKU_FULL_TMP_1.KUUML IGNORE NULLS) OVER (PARTITION BY MSKU_FULL_TMP_1.MATNR, MSKU_FULL_TMP_1.WERKS, MSKU_FULL_TMP_1.SOBKZ, MSKU_FULL_TMP_1.KUNNR ORDER BY MSKU_FULL_TMP_1."EVENTTIME" DESC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS KUUML
  ,MSKU_FULL_TMP_1."EVENTTIME"
FROM MSKU_FULL_TMP_1
ORDER BY MSKU_FULL_TMP_1.EVENTTIME ASC
);

SELECT ANALYZE_STATISTICS('MSKU_FULL_TMP_2');

-----------------------------------------------------------------------------
-- Step 6: Aggregate up to the Material Number and filter timeframe based on start date variable.
-----------------------------------------------------------------------------
DROP TABLE IF EXISTS MSKU_FULL_TMP_3;

CREATE TABLE MSKU_FULL_TMP_3 AS (
SELECT
   MSKU_FULL_TMP_2.MANDT
   ,MSKU_FULL_TMP_2.MATNR
   ,MSKU_FULL_TMP_2.WERKS
   ,MSKU_FULL_TMP_2.SOBKZ
   ,MSKU_FULL_TMP_2.KUNNR
   ,SUM(MSKU_FULL_TMP_2.KULAB) AS KULAB
   ,SUM(MSKU_FULL_TMP_2.KUINS) AS KUINS
   ,SUM(MSKU_FULL_TMP_2.KUEIN) AS KUEIN
   ,SUM(MSKU_FULL_TMP_2.KUUML) AS KUUML
   ,MSKU_FULL_TMP_2."EVENTTIME"
FROM MSKU_FULL_TMP_2
WHERE MSKU_FULL_TMP_2."EVENTTIME" >= ADD_MONTHS(CURRENT_DATE,(-12 * 23 - 1)) -- lower threshold of timeseries.
GROUP BY MANDT, MATNR, WERKS, SOBKZ, KUNNR, EVENTTIME
ORDER BY MSKU_FULL_TMP_2.EVENTTIME ASC
);

SELECT ANALYZE_STATISTICS('MSKU_FULL_TMP_3');

-----------------------------------------------------------------------------
-- Step 7: Finalize table by adding currency and unit conversions.
-----------------------------------------------------------------------------


DROP TABLE IF EXISTS CONSIGNMENT_STOCK_HISTORY;


CREATE TABLE CONSIGNMENT_STOCK_HISTORY AS (
 SELECT *,
 YEAR(EVENTTIME) AS JAHR,
 MONTH(EVENTTIME) AS MONAT
  FROM "MSKU_FULL_TMP_3"
  WHERE MSKU_FULL_TMP_3.SOBKZ = 'W' AND YEAR(EVENTTIME)> 2021
);

-----------------------------------------------------------------------------
-- CLEAN UP --
-----------------------------------------------------------------------------
DROP TABLE IF EXISTS "MSKUH_TMP";
DROP TABLE IF EXISTS "MSKU_FULL_TMP";
DROP TABLE IF EXISTS "TMP_CALENDAR";
DROP TABLE IF EXISTS "CALENDAR";
DROP TABLE IF EXISTS "MSKU_FULL_TMP_DEDUPLICATED";
DROP TABLE IF EXISTS "MARC_DATES";
DROP TABLE IF EXISTS "MSKU_FULL_TMP_1";
DROP TABLE IF EXISTS "MSKU_FULL_TMP_2";
DROP TABLE IF EXISTS "MSKU_FULL_TMP_3";
CUSTOMERS table

The SAP ECC table that supplies data for this transformation is KNA1.

The CUSTOMERS table is a helper table for the augmented attribute that is used to define contractual parameters for each customer.

Transformation 6: CUSTOMERS table
DROP TABLE IF EXISTS CONSIGNMENT_INPUT_TEMP;
CREATE TABLE CONSIGNMENT_INPUT_TEMP AS
SELECT DISTINCT MANDT, KUNNR
FROM CONSIGNMENT_STOCK_HISTORY;

DROP TABLE IF EXISTS CUSTOMERS;
CREATE TABLE CUSTOMERS AS
SELECT CONSIGNMENT_INPUT_TEMP.MANDT, CONSIGNMENT_INPUT_TEMP.KUNNR, KNA1.NAME1 AS NAME
FROM CONSIGNMENT_INPUT_TEMP
LEFT JOIN KNA1 ON
CONSIGNMENT_INPUT_TEMP.MANDT =  KNA1.MANDT
AND CONSIGNMENT_INPUT_TEMP.KUNNR = KNA1.KUNNR;

-----------------------------------------------------------------------------
-- CLEAN UP --
-----------------------------------------------------------------------------
DROP TABLE IF EXISTS CONSIGNMENT_INPUT_TEMP;
CUSTOMER_MATERIALS table

The CUSTOMER_MATERIALS table references the CONSIGNMENT_STOCK_HISTORY table. It’s a helper table used for augmented attributes and for simplifying PQL queries. The data at this level represents the default lowest level of aggregation in the Customer Consignment Stock app (material + customer + plant).

Transformation 7: CUSTOMER_MATERIALS table
DROP TABLE IF EXISTS CUSTOMER_MATERIALS CASCADE;
CREATE TABLE CUSTOMER_MATERIALS AS
SELECT DISTINCT MANDT, KUNNR, MATNR, WERKS
FROM CONSIGNMENT_STOCK_HISTORY;
CS_VBAK (order header) table

The SAP ECC tables that supply data for this transformation are VBAK, TVAKT, TVKOT, and T001.

If your data pool already contains the table O2C_VBAK from the standard Celonis data model for Order to Cash, you can reuse that table instead of creating it. To reuse the O2C_VBAK table, add transformation 8a. Otherwise, create the CS_VBAK table by adding transformation 8b.

Transformation 8a: CS_VBAK table reusing O2C_VBAK
DROP TABLE IF EXISTS "CS_VBAK";

CREATE TABLE "CS_VBAK" AS (
SELECT * FROM "O2C_VBAK"
 	WHERE "O2C_VBAK"."MANDT"||"O2C_VBAK"."VBELN" IN (SELECT "VBAP"."MANDT"||"VBAP"."VBELN" FROM "CS_VBAP" AS "VBAP")
);
Transformation 8b: CS_VBAK table
DROP TABLE IF EXISTS "CS_VBAK";
CREATE TABLE "CS_VBAK" AS (
SELECT
   "VBAK".*,
   CAST("VBAK"."ERDAT" AS DATE) AS "TS_ERDAT",
   CAST("VBAK"."VDATU" AS DATE) AS "TS_VDATU",
   IFNULL("TVAKT"."BEZEI",'') AS "AUART_TEXT",
   IFNULL("TVKOT"."VTEXT",'') AS "VKORG_TEXT",
   IFNULL("T001"."BUTXT",'') AS "BUKRS_TEXT"
FROM
   "VBAK" AS "VBAK"
   LEFT JOIN "TVAKT" AS "TVAKT" ON 1=1
      	AND "VBAK"."MANDT" = "TVAKT"."MANDT"
     	AND "VBAK"."AUART" = "TVAKT"."AUART"
      	AND "TVAKT"."SPRAS" = 'E'
   LEFT JOIN "TVKOT" ON 1=1
   	AND "VBAK"."MANDT"="TVKOT"."MANDT"
     	AND "VBAK"."VKORG" = "TVKOT"."VKORG"
      	AND "TVKOT"."SPRAS" = 'E'
   LEFT JOIN "T001" ON 1=1
    	AND "VBAK"."MANDT" = "T001"."MANDT"
    	AND "VBAK"."BUKRS_VF" = "T001"."BUKRS"
WHERE VBAK.MANDT||VBAK.VBELN IN (SELECT VBAP.MANDT||VBAP.VBELN FROM "CS_VBAP" AS VBAP)
);
CS_VBAP (order items) table

The SAP ECC tables that supply data for this transformation are VBAP, VBAK, MAKT, TVAPT, and TVAGT.

The two transformations here create the CS_VBAP table and a temporary table with the converted net order value. You can put them both under the same transformation or create two separate transformations.

If your data pool already contains the table O2C_VBAP from the standard Celonis data model for Order to Cash, you can reuse the table instead of creating one. To reuse the O2C_VBAP table, add transformation 9a. Otherwise, create the CS_VBAP table by adding transformations 9b and 9c.

Transformation 9a: CS_VBAP table reusing O2C_VBAP
DROP TABLE IF EXISTS "CS_VBAP";

CREATE TABLE "CS_VBAP" AS (
   SELECT * FROM "O2C_VBAP"
   WHERE "O2C_VBAP"."MANDT"||"O2C_VBAP"."VBELN"||"O2C_VBAP"."POSNR" IN (SELECT "CM"."MANDT"||"CM"."KDAUF"||"CM"."KDPOS" FROM "CONSIGNMENT_MOVEMENTS" AS "CM")
);
Transformation 9b: Temporary table VBAP_CURR_TMP
--Create temp table with converted net order value
DROP TABLE IF EXISTS "VBAP_CURR_TMP";
CREATE TABLE "VBAP_CURR_TMP" AS(
   SELECT
   "VBAP"."MANDT"
   ,"VBAP"."VBELN"
   ,"VBAP"."POSNR"
   ,CASE
       WHEN "VBAP"."WAERK" = '<%=currency%>' THEN "VBAP"."NETWR"*COALESCE("TCURX"."TDEC",1)
       WHEN "TCURR_CC"."UKURS" < 0 THEN ("VBAP"."NETWR"*COALESCE("TCURX"."TDEC",1))*(1/ABS(TCURR_CC.UKURS)/(CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END))*(CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
       WHEN "TCURR_CC"."UKURS" > 0 THEN ("VBAP"."NETWR"*COALESCE("TCURX"."TDEC",1))*(TCURR_CC.UKURS/CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END*CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END) 
   END AS "NETWR_CONVERTED"
   ,CASE
       WHEN "VBAP"."WAERK" = '<%=currency%>' THEN "VBAP"."NETPR"*COALESCE("TCURX"."TDEC",1)
       WHEN "TCURR_CC"."UKURS" < 0 THEN ("VBAP"."NETPR"*COALESCE("TCURX"."TDEC",1))*(1/ABS(TCURR_CC.UKURS)/(CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END))*(CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
       WHEN "TCURR_CC"."UKURS" > 0 THEN ("VBAP"."NETPR"*COALESCE("TCURX"."TDEC",1))*(TCURR_CC.UKURS/CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END*CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END) 
   END AS "NETPR_CONVERTED"   
   ,CASE
       WHEN "VBAP"."WAERK" = '<%=currency%>' THEN "VBAP"."WAVWR"*COALESCE("TCURX"."TDEC",1)
       WHEN "TCURR_CC"."UKURS" < 0 THEN ("VBAP"."WAVWR"*COALESCE("TCURX"."TDEC",1))*(1/ABS(TCURR_CC.UKURS)/(CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END))*(CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END)
       WHEN "TCURR_CC"."UKURS" > 0 THEN ("VBAP"."WAVWR"*COALESCE("TCURX"."TDEC",1))*(TCURR_CC.UKURS/CASE WHEN COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.FFACT, TCURR_CC.FFACT) END*CASE WHEN COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT, 0) = 0 THEN 1 ELSE COALESCE(TCURF_CC.TFACT, TCURR_CC.TFACT) END) 
   END AS "WAVWR_CONVERTED"       
   FROM "VBAP"
   LEFT JOIN "TCURR_CC" AS "TCURR_CC" ON 1=1
           AND "TCURR_CC"."MANDT" = "VBAP"."MANDT"
           AND "TCURR_CC"."TCURR" = '<%=currency%>'
           AND "TCURR_CC"."FCURR" = "VBAP"."WAERK"
           AND "TCURR_CC"."KURST" = 'M'
           AND "VBAP"."ERDAT" >= "TCURR_CC"."VALID_START" AND "VBAP"."ERDAT" < "TCURR_CC"."VALID_END"
   LEFT JOIN "TCURF_CC" AS "TCURF_CC" ON 1=1
           AND "TCURR_CC"."MANDT" = "TCURF_CC"."MANDT"
           AND "TCURR_CC"."TCURR" = "TCURF_CC"."TCURR"
           AND "TCURR_CC"."FCURR" = "TCURF_CC"."FCURR"
           AND "TCURR_CC"."KURST" = "TCURF_CC"."KURST"
           AND "VBAP"."ERDAT" >= "TCURF_CC"."VALID_START" AND "VBAP"."ERDAT" < "TCURF_CC"."VALID_END"
   LEFT JOIN (
           SELECT
               "TCURX"."CURRKEY"
               ,CAST("TCURX"."CURRDEC" AS INT) AS "CURRDEC"
               ,POWER(CAST(10 AS FLOAT),(2-"CURRDEC")) AS "TDEC"
           FROM "TCURX") AS "TCURX" ON 1=1
           AND "VBAP"."WAERK" = "TCURX"."CURRKEY"
);
-- Analyze (create) statistics for created temporary table
SELECT ANALYZE_STATISTICS('VBAP_CURR_TMP');
Transformation 9c: CS_VBAP table
DROP TABLE IF EXISTS "CS_VBAP";
CREATE TABLE "CS_VBAP" AS (
SELECT
       "VBAP".*,
       "VBAP"."MANDT"||"VBAP"."VBELN"||"VBAP"."POSNR" as "_CASE_KEY",
       CAST("VBAP"."ERDAT" AS DATE) AS "TS_ERDAT",
       IFNULL("MAKT"."MAKTX",'') AS "MATNR_TEXT",
       IFNULL("TVAPT"."VTEXT",'') AS "PSTYV_TEXT",
       IFNULL("TVAGT"."BEZEI",'') AS "ABGRU_TEXT",
       "VBAP_CURR_TMP"."NETWR_CONVERTED" AS "NETWR_CONVERTED",
       "VBAP_CURR_TMP"."NETPR_CONVERTED" AS "NETPR_CONVERTED"
FROM "VBAP" AS "VBAP"
   INNER JOIN "VBAK" ON 1=1
       	AND "VBAK"."MANDT" = "VBAP"."MANDT"
          AND "VBAK"."VBELN" = "VBAP"."VBELN"
       	AND "VBAK"."VBTYP" = 'C'
          LEFT JOIN "VBAP_CURR_TMP" AS "VBAP_CURR_TMP" ON 1=1
         	AND "VBAP"."MANDT" = "VBAP_CURR_TMP"."MANDT"
         	AND "VBAP"."VBELN" = "VBAP_CURR_TMP"."VBELN"
         	AND "VBAP"."POSNR" = "VBAP_CURR_TMP"."POSNR" 
   LEFT JOIN "MAKT" ON 1=1
       AND "VBAP"."MANDT" = "MAKT"."MANDT"
     	AND "VBAP"."MATNR" = "MAKT"."MATNR"
   	AND "MAKT"."SPRAS" = 'E'
   LEFT JOIN "TVAPT" ON 1=1
     	AND "VBAP"."MANDT" = "TVAPT"."MANDT"
     	AND "VBAP"."PSTYV" = "TVAPT"."PSTYV"
       AND "TVAPT"."SPRAS" = 'E'
   LEFT JOIN "TVAGT" ON 1=1
       AND "VBAP"."MANDT" = "TVAGT"."MANDT"
     	AND "VBAP"."ABGRU" = "TVAGT"."ABGRU"
       AND "TVAGT"."SPRAS" = 'E'
WHERE VBAP.MANDT||VBAP.VBELN||VBAP.POSNR IN (SELECT CM.MANDT||CM.KDAUF||CM.KDPOS FROM "CONSIGNMENT_MOVEMENTS" AS CM)

);
CS_VBEP (schedule lines) table

The SAP ECC table that supplies data for this transformation is VBEP.

If your data pool already contains the table O2C_VBEP from the standard Celonis data model for Order to Cash, you can reuse that table instead of creating one. To reuse the O2C_VBEP table, add transformation 10a. Otherwise, create the CS_VBEP table by adding transformation 10b.

Transformation 10a: CS_VBEP table reusing O2C_VBEP
DROP TABLE IF EXISTS "CS_VBEP";

CREATE TABLE "CS_VBEP" AS (
SELECT * FROM "O2C_VBEP"
 	WHERE "O2C_VBEP"."MANDT"||"O2C_VBEP"."VBELN"||"O2C_VBEP"."POSNR" IN (SELECT "VBAP"."MANDT"||"VBAP"."VBELN"||"VBAP"."POSNR" FROM "CS_VBAP" AS "VBAP")
);
Transformation 10b: CS_VBEP table
DROP TABLE IF EXISTS "CS_VBEP";

CREATE TABLE "CS_VBEP" AS (
   SELECT
	"VBEP".*,
       CAST("VBEP"."EDATU" AS DATE) AS "TS_EDATU",
       CAST("VBEP"."LDDAT" AS DATE) AS "TS_LDDAT",
       CAST("VBEP"."MBDAT" AS DATE) AS "TS_MBDAT",
       CAST("VBEP"."TDDAT" AS DATE) AS "TS_TDDAT",
       CAST("VBEP"."WADAT" AS DATE) AS "TS_WADAT"
    FROM "VBEP" AS "VBEP"
   WHERE VBEP.MANDT||VBEP.VBELN||VBEP.POSNR IN (SELECT VBAP.MANDT||VBAP.VBELN||VBAP.POSNR FROM "CS_VBAP" AS VBAP)
);
CS_VBRP (invoice items) table

The SAP ECC table that supplies data for this transformation is VBRP.

If your data pool already contains the table O2C_VBRP from the standard Celonis data model for Order to Cash, you can reuse that table instead of creating it. To reuse the O2C_VBRP table, add transformation 11a. Otherwise, create the VBRP table by adding transformation 11b.

Transformation 11a: CS_VBRP table reusing O2C_VBRP
DROP TABLE IF EXISTS "CS_VBRP";

CREATE TABLE "CS_VBRP" AS (
SELECT * FROM "O2C_VBRP"
 	WHERE "O2C_VBRP"."MANDT"||"O2C_VBRP"."AUBEL"||"O2C_VBRP"."AUPOS" IN (SELECT "VBAP"."MANDT"||"VBAP"."VBELN"||"VBAP"."POSNR" FROM "CS_VBAP" AS "VBAP")
);
Transformation 11b: CS_VBRP table
DROP TABLE IF EXISTS "CS_VBRP";

CREATE TABLE "CS_VBRP" AS(
   	SELECT "VBRP".*
 	FROM "VBRP" AS "VBRP"
WHERE VBRP.MANDT || VBRP.AUBEL || VBRP.AUPOS IN (SELECT VBAP.MANDT || VBAP.VBELN || VBAP.POSNR FROM "CS_VBAP" AS VBAP)
);
Data Model for the case-centric Customer Consignment Stock app

When you’ve extracted the required tables, created the transformations, and run them, use the instructions in this section to create and load the data model shown in the diagram.

custcons_cc_datamodel.png
  1. In Data Integration, go to your data pool and click the Data Models area. Click Add Data Model to create a new data model. Give it a suitable name and click Save.

  2. Select all of the tables you created with the transformations, which are: IM_MARC, CONSIGNMENT_MOVEMENTS, CONSIGNMENT_STOCK_HISTORY, CUSTOMERS, CUSTOMER_MATERIALS, CS_VBAK, CS_VBAP, CS_VBEP, and CS_VBRP.

  3. Create an alias for the tables with an CS_ or IM_ prefix, and give them the standard SAP ECC table names without the prefix (MARC, VBAK, VBAP, VBEP, and VBRP). Click the context menu (the three dots) at the top right of each table to create its alias.

  4. Join the tables with the joins shown in the following table.

    Table 1

    Join type

    Table 2

    On columns

    VBAK

    1:n

    VBAP

    MANDT, VBELN

    VBAP

    1:n

    VBEP

    MANDT, VBELN, POSNR

    VBAP

    1:n

    VBRP

    MANDT, AUBEL/VBELN, AUPOS/POSNR

    VBAP

    1:n

    CONSIGNMENT_MOVEMENTS

    MANDT, VBELN/KDAUF, POSNR/KDPOS

    CONSIGNMENT_STOCK_HISTORY

    1:n

    CONSIGNMENT_MOVEMENTS

    MANDT, KUNNR, MATNR, WERKS, JAHR, MONAT

    CUSTOMER_MATERIALS

    1:n

    CONSIGNMENT_STOCK_HISTORY

    MANDT, KUNNR, MATNR, WERKS

    MARC

    1:n

    CUSTOMER_MATERIALS

    MANDT, WERKS, MATNR

    CUSTOMERS

    1:n

    CUSTOMER_MATERIALS

    MANDT, KUNNR

  5. Load the finished data model.

Now you can install the case-centric Customer Consignment Stock app, following the instructions in Installing the case-centric Customer Consignment Stock app.