Using the Currency Conversion object
Using the Currency Conversion object is a fairly simple process. Once the object is created in the data model then loaded in, the PQL operator CURRENCY_CONVERT
allows us to reflect the logic of the different parameters (exchange rate between currencies, time frame, and currencies being converted). Once all values are plugged into the operator, the corresponding value can be calculated.
Error handling
Consider a situation where CompanyXYZ wants to see Sys1 results displayed in EUR. The main currency that Sys1 operates in is AUD and does not have a conversion rate to EUR. However, Sys1 does have a third currency, USD, which does have a conversion rate to EUR, and AUD also has a conversion rate to USD. The Currency Conversion object can be adjusted manually in the Celonis Platform to convert the starting currency (AUD) to the target currency (EUR). The example below includes sample code that can be used to resolve this scenario.
There may be a different situation where CompanyXYZ wants to see Sys3 results displayed in EUR. However, Sys3 currency tables eitherdon't have a currency that converts to EUR, or if there is a currency that converts to EUR, there is no path from NOK to that intermediary currency. Sys3 does not have a path to go from NOK to an intermediate currency and finally to EUR. If there is no pair of currencies to act as an intermediate between the starting currency and the desired target currency, then the data needs another extraction without any limitations to find an intermediary currency pair.
Example - Intermediary currency pair
When a currency does not have a direct conversion rate with another currency and the inverse of the combination is also not available, then the best strategy is to find an intermediary currency pair as described above. This solution is not used in the base CurrencyConversion object as it would inflate the data unnecessarily for most cases.
Below is an example of a second insert that would be added to a customer’s CurrencyConversion object when using OracleEBS to convert all currencies into AUD:
WITH "CTE_INTERMEDIARY" AS ( SELECT 'CurrencyConversion_' || "GL_DAILY_RATES"."FROM_CURRENCY" || '_' || "GL_DAILY_RATES"."TO_CURRENCY" || '_' || "GL_DAILY_RATES"."CONVERSION_DATE" || '_EBS' AS "ID", || '_EBS' "GL_DAILY_RATES"."FROM_CURRENCY" AS "FromCurrency", "GL_DAILY_RATES"."TO_CURRENCY" AS "ToCurrency", CAST("GL_DAILY_RATES"."CONVERSION_DATE" AS TIMESTAMP) AS "FromDate", CAST(CAST("GL_DAILY_RATES"."CONVERSION_DATE" AS DATE) + CAST('23:59:59' AS TIME) AS TIMESTAMP) AS "ToDate", "GL_DAILY_RATES"."CONVERSION_RATE" AS "Rate", 'Oracle' AS "SourceSystemType", NULL AS "SourceSystemInstance", "GL_DAILY_RATES"."CONVERSION_TYPE" AS "ExchangeRateType" FROM <%=DATASOURCE:ORACLE_ORACLE%>."GL_DAILY_RATES" AS "GL_DAILY_RATES" WHERE "GL_DAILY_RATES"."CONVERSION_TYPE" = 'Corporate' -- TODO: Parameter (ExchangeRateType = 'Corporate') AND "GL_DAILY_RATES"."TO_CURRENCY" = 'USD' ) SELECT 'CurrencyConversion_' || "GL_DAILY_RATES"."FROM_CURRENCY" || '_' || "GL_DAILY_RATES"."TO_CURRENCY" || '_' || "GL_DAILY_RATES"."CONVERSION_DATE" || '_EBS' AS "ID", "CTE_INTERMEDIARY"."FromCurrency" AS "FromCurrency", "GL_DAILY_RATES"."TO_CURRENCY" AS "ToCurrency", CAST("GL_DAILY_RATES"."CONVERSION_DATE" AS TIMESTAMP) AS "FromDate", CAST(CAST("GL_DAILY_RATES"."CONVERSION_DATE" AS DATE) + CAST('23:59:59' AS TIME) AS TIMESTAMP) AS "ToDate", "CTE_INTERMEDIARY"."Rate" * "GL_DAILY_RATES"."CONVERSION_RATE" AS "Rate", 'Oracle' AS "SourceSystemType", NULL AS "SourceSystemInstance", "GL_DAILY_RATES"."CONVERSION_TYPE" AS "ExchangeRateType" FROM <%=DATASOURCE:ORACLE_ORACLE%>."GL_DAILY_RATES" AS "GL_DAILY_RATES" LEFT JOIN "CTE_INTERMEDIARY" ON "CTE_INTERMEDIARY"."FromDate" = CAST("GL_DAILY_RATES"."CONVERSION_DATE" AS TIMESTAMP) AND "CTE_INTERMEDIARY"."ToCurrency" = "GL_DAILY_RATES"."FROM_CURRENCY" WHERE "GL_DAILY_RATES"."CONVERSION_TYPE" = 'Corporate' AND "GL_DAILY_RATES"."FROM_CURRENCY" = 'USD' AND "GL_DAILY_RATES"."TO_CURRENCY" = 'AUD';
In this example, note that the CTE is converting all currencies into USD, then into AUD. This is not strictly necessary and if there is only one currency that is desired to be converted to the final target currency, a filter can be added on FROM_CURRENCY in the CTE. The CTE will always have the TO_CURRENCY as the intermediate currency. Then, the main insert statement will have the FROM_CURRENCY as the intermediate currency and the TO_CURRENCY as the final desired target currency.
Code explanation
Summarizing explanation of the object creation code.
The first part is specific per system, as it considers the raw system table as input. Output of the system specific section is the creation of two CTEs, CTE_DIRECT_CONVERSION and CTE_INVERSE_CONVERSION.
SAP
Required tables:
TCURR
TCURF
TCURX
Logic:
Self-join TCURF to get all Start and End times for it.
CTE_TCURF_TMP
Select * from TCURF
DENSE_RANK() for easier join instead of all entries
ROW_NUMBER() to do the offset by validity date
CTE_TCURF_CC
Just converts GDATU to a readable timestamp format and adds it as VALID_START
CTE_TCURF_CC_2
Handles the self-join based on the DENSE_RANK as key and ROW_NUMBER as the offset to get all valid start/end time pairs
CTE_TCURR_TMP
Select * from TCURR
DENSE_RANK() for easier join instead of all entries
ROW_NUMBER() to do the offset by validity date
CTE_TCURR_CC
Just converts GDATU to a readable timestamp format and adds it as VALID_START
CTE_TCURR_CC_2
Handles the self-join based on the DENSE_RANK as key and ROW_NUMBER as the offset to get all valid start/end time pairs
Build helper tables with all days since beginning of time until now() (TCURR/TCURF GDATU are not the same so this is needed).
CTE_DATE_RANGE
Set starting point, set end point (now()) and then timestamp every day
Get TDEC value to get true position of comma for converted values.
CTE_TCURX
Standard is 2 so the offset is then based off of it
Build the whole thing.
Currency_Conversion
ID: Mix of everything
FromCurrency: TCURR.FCURR
ToCurrency: TCURR.TCURR
FromDate: TCURX ._DATE
ToDate: TCURX ._DATE
Rate: please find explanation below
Rate calculation
UKURS
UKURS < 0 means we need inverse calculation (1/ABS(UKURS))
UKURS > 0 means we can directly use it
Multiply the TCURX.TDEC offset
Divide FromCurrency Ratio TCURF.FFACT
Multiply ToCurrency Ratio TCURF.TFACT
CTE_DIRECT_CONVERSION
ID composed based on order: "TCURR_CC_2"."FCURR" || '_' || "TCURR_CC_2"."TCURR" || '_' || "_DATE" || '_' || 'SAP' || '_' || "TCURR_CC_2"."MANDT"
FromCurrency filled with actual FromCurrency value in raw table: "TCURR_CC_2"."FCURR"
ToCurrency filled with actual ToCurrency value in raw table: "TCURR_CC_2"."TCURR"
Rate calculation (direct - formula in point 5)
Flag inserted in ‘ConversionType’ column with value ‘Direct’
Filter applied: MANDT IS NOT NULL
CTE_INVERSE_CONVERSION
ID composed based on order: "TCURR_CC_2"."TCURR" || '_' || "TCURR_CC_2"."FCURR" || '_' || "_DATE" || '_' || 'SAP' || '_' || "TCURR_CC_2"."MANDT"
FromCurrency filled with actual ToCurrency value in raw table: "TCURR_CC_2"."TCURR"
ToCurrency filled with actual FromCurrency value in raw table: "TCURR_CC_2"."FCURR"
Inverse rate calculation ( 1/ formula in point 5)
Flag inserted in ‘ConversionType’ column with value ‘Inverse’
Filter applied: MANDT IS NOT NULL
Oracle EBS
Required tables:
GL_DAILY_RATES
Logic:
CTE_DIRECT_CONVERSION
ID composed based on order: "GL_DAILY_RATES"."FROM_CURRENCY" || '_' || "GL_DAILY_RATES"."TO_CURRENCY" || '_' || "GL_DAILY_RATES"."CONVERSION_DATE" || '_EBS'
FromCurrency filled with actual FromCurrency value in raw table: "GL_DAILY_RATES"."FROM_CURRENCY"
ToCurrency filled with actual ToCurrency value in raw table: "GL_DAILY_RATES"."TO_CURRENCY"
Rate - Direct: "GL_DAILY_RATES"."CONVERSION_RATE"
Flag inserted in ‘ConversionType’ column with value ‘Direct’
No filter applied
CTE_INVERSE_CONVERSION
ID composed based on order: "GL_DAILY_RATES"."TO_CURRENCY" || '_' || "GL_DAILY_RATES"."FROM_CURRENCY" || '_' || "GL_DAILY_RATES"."CONVERSION_DATE" || '_EBS'
FromCurrency filled with actual ToCurrency value in raw table: "GL_DAILY_RATES"."TO_CURRENCY"
ToCurrency filled with actual FromCurrency value in raw table: "GL_DAILY_RATES"."FROM_CURRENCY"
Rate - Inverse: 1 / "GL_DAILY_RATES"."CONVERSION_RATE"
Flag inserted in ‘ConversionType’ column with value ‘Inverse’
No filter applied
Oracle Fusion
Required tables:
FscmTopModelAM_FinExtractAM_GlBiccExtractAM_DailyRateExtractPVO
Logic:
CTE_DIRECT_CONVERSION
ID composed based on order: "DailyRateExtractPVO"."DailyRateFromCurrency" || '_' || "DailyRateExtractPVO"."DailyRateToCurrency" || '_' || "DailyRateExtractPVO"."DailyRateConversionDate" || '_FUSION'
FromCurrency filled with actual FromCurrency value in raw table: "DailyRateExtractPVO"."DailyRateFromCurrency"
ToCurrency filled with actual ToCurrency value in raw table: "DailyRateExtractPVO"."DailyRateToCurrency"
Rate - Direct: "DailyRateExtractPVO"."DailyRateConversionRate"
Flag inserted in ‘ConversionType’ column with value ‘Direct’
No filter applied
CTE_INVERSE_CONVERSION
ID composed based on order: "DailyRateExtractPVO"."DailyRateToCurrency" || '_' || "DailyRateExtractPVO"."DailyRateFromCurrency" || '_' || "DailyRateExtractPVO"."DailyRateConversionDate" || '_FUSION'
FromCurrency filled with actual ToCurrency value in raw table: "DailyRateExtractPVO"."DailyRateToCurrency"
ToCurrency filled with actual FromCurrency value in raw table: "DailyRateExtractPVO"."DailyRateFromCurrency"
Rate - Inverse: 1 / "DailyRateExtractPVO"."DailyRateConversionRate"
Flag inserted in ‘ConversionType’ column with value ‘Inverse’
No filter applied
System Agnostic [SAP + EBS + Fusion]
This part of the code is following the previously explained one per system, and it is in common for all the systems.
Required CTEs previously created:
CTE_DIRECT_CONVERSION
CTE_INVERSE_CONVERSION
Logic:
CTE_DIRECT_CONVERSION_TO
Select * from CTE_DIRECT_CONVERSION
Filter on ToCurrency for combination (ExchangeRateType, CurrencyKey) OR (ExchangeRateType_2, CurrencyKey_2)
CTE_DIRECT_CONVERSION_FROM
Select * from CTE_DIRECT_CONVERSION
Filter on FromCurrency for combination (ExchangeRateType, CurrencyKey) OR (ExchangeRateType_2, CurrencyKey_2)
CTE_INVERSE_CONVERSION_TO
Select * from CTE_INVERSE_CONVERSION
Filter on ToCurrency for combination (ExchangeRateType, CurrencyKey) OR (ExchangeRateType_2, CurrencyKey_2)
CTE_INVERSE_CONVERSION_FROM
Select * from CTE_INVERSE_CONVERSION
Filter on FromCurrency for combination (ExchangeRateType, CurrencyKey) OR (ExchangeRateType_2, CurrencyKey_2)
CTE_CONVERSION_ALL
Unioning following selects:
Select * from CTE_DIRECT_CONVERSION_TO
Select * from CTE_DIRECT_CONVERSION_FROM excluding combinations already in CTE_DIRECT_CONVERSION_TO (ExchangeRateType not considered when excluding - in case of overlap ExchangeRateType in previous select predominate)
Select * from INVERSE_CONVERSION_FROM excluding cases in CTE_DIRECT_CONVERSION_TO and CTE_DIRECT_CONVERSION_FROM (ExchangeRateType not considered when excluding - in case of overlap ExchangeRateType in previous select predominate)
Select * from INVERSE_CONVERSION_TO excluding cases in CTE_DIRECT_CONVERSION_TO and CTE_DIRECT_CONVERSION_FROM and INVERSE_CONVERSION_FROM (ExchangeRateType not considered when excluding - in case of overlap ExchangeRateType in previous select predominate)