Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 34

BPC Script logic for Dummies,

chapter 1
When I started in BPC I couldn't find much information about Logic Scripts,
and I accidentally came across a blog that changed my life, which is why I
want to share it for those of us who are in the SAP BPC world.

Although I have a long experience with BPC, if someone asks me to write a


Logic script, I may not write that code in 10 minutes. It's not an issue that I'm
aware of, but writing a script requires the logic to understand your financial
requirements and your application's knowledge, such as account member ID
and property name.

There are some documents and help files on script logic and HTG, but end
users may feel that it is not easy. I agree with that, but if you understand its
structure and concept, I can guarantee that you can read and understand what
it means and what the purpose of that script logic is. In addition to that, it is
possible to enable it to modify or create it. It's the same as writing a book is
not easy, but reading a book is a different story.
Let's learn it step by step.

It comprises 3 logical parts.


Logic is a special script engine and consists of 3 parts. Scoping, calculation
(create/register) and writing.

1 reach

BPC is based on NW BI or MSAS which has a large amount of data.


Therefore, if you do not specify the scope, it will take a long time to read the
data.
Let's say you need to calculate 2011.JAN, actuals and just one account like
'Discounted External Sales' based on External Sales.
How can we analyze this from a large database?
The answer is…. * XDIM_MEMBERSET
* XDIM_MEMBERSET is using scope data for each dimension.

Here is the grammar of XDIM_MEMBERSET.


* XDIM_MEMBERSET <DIMENSIONNAME> = <MEMBERNAME 1>,
<MEMBERNAME 2> … <MEMBERNAME n>

Now, let's look at the scope above.


For scope 2011.JAN, *XDIM_MEMBERSET TIMEDIM = 2011.JAN
For actual scope, *XDIM_MEMBERSET CATEGORYDIM = CURRENT
For external sales scope, *XDIM_MEMBERSET ACCOUNTDIM =
EXTSALES
(Note: We need to perform outside sales scoping because discounted outside
sales will be calculated based on outside sales.)

2. Now, we have just finalized the scope


so it is time to calculate (create) data.

Unlike other script engines, there is no temporary variable in the logical script
engine, so it will create a record that has the same fact table structure and
replace or change its value by the '*REC' command. (Note: *REC means
'Record'.)
Here is the grammar of the *REC statement
*REC [([FACTOR | EXPRESSION = {Expression} [, {dim1} = {member},
{dim2} =?)]

Using that grammar, we can make our script like below.

*REC (FACTOR = 0.9, ACCOUNT = “DISCOUNT_EXTSALES”)


Which means multiply by 0.9 to the current scope record and replace the
account member with DiSCOUNT_EXTSALES
Here is an example of what happens with the above statement.
<Scoped record>
EXTSALES, 2011.JAN, CURRENT, 10000
<Log generated>
DISCOUNT_EXTSALES, 2011.JAN, CURRENT, 9000
What if you want to place the generated record in the BUDGET category?
So the statement should be
* REC (FACTOR = 0.9, ACCOUNT = “DISCOUNT_EXTSALES”,
CATEGORY = “ BUDGET ”)
Now you want to put 80% value in FORECAST at the same time. What
should we do?
We can use another *REC statement at the same time.
*REC (FACTOR = 0.9, ACCOUNT = “DISCOUNT_EXTSALES”,
CATEGORY = ” BUDGET “)
*REC (FACTOR = 0.8 , ACCOUNT = “DISCOUNT_EXTSALES”,
CATEGORY = ” FORECAST “)
<Scoped record>
EXTSALES, 2011.JAN, CURRENT, 10000
<Log generated>
DISCOUNT_EXTSALES, 2011.JAN, BUDGET , 9000
DISCOUNT_EXTSALES, 2011.JAN, FORECAST , 8000

It is easier? I hope so
Please note below rule.

 Each REC instruction generates ONE new record.

 Each source record can generate as many records as you want.

o It means you have a scope of 1 record, but you can create multiple
records using this.

 Currency translation is the best example because you need multiple


currencies converted using a local currency record.

 Therefore, you can imagine that there will be multiple * REC statements
in your currency conversion script logic.

 The destination cell can be the same. All values will be accumulated.

 The *REC statement will generate a new record, so it doesn't matter, even
if the destination is the same.
3. As a final step, we need to write data
to the database.

The script declaration is really simple.


*COMMIT

Fortunately, it has no parameters. Just use * COMMIT .


When the BPC script engine executes *COMMIT, the generated records will
be posted and applied, using the BPC submit engine, which is the same engine
that submits Excel workbook data.

We review three main parts of the Logic script script in BPC.

I explained 3 basic parts of script logic in the last post BPC Script logic for Dummies, chapter

1. . It was scope, calculation and writing.

We will find more advanced features for outreach in this post.

1. Scope using member property

We found out how to use XDIM_MEMBERSET last time. *XDIM_MEMBERSET is for scope

based on member ID.


What if the user wants to include members in a specific property value? For example, a user

wants to filter which members of the account dimension are active.

To achieve this, we need to use the ACCTYPE property which has the value of account type.

AST is the value for the ASSET account. (Note: The value is based on the BPC APSHELL.)

The command is *XDIM_FILTER

The usage is *XDIM_FILTER <DIMENSIONNAME> = [DIMENSIONName].Properties

(“Property Name”) = “Property Value”

So the above example can be written as follows.

*XDIM_FILTER ACCOUNT = [account].properties(ACCTYPE = 'AST')

Let's say the account dimension has 3 members like below.

ID ACCTYPE

Extsales INC

CASH AST

TAXES EXP

NETINCOME INC

Then *XDIM_FILTER_ACCOUNT will select only the CASH member.

Suppose you have already used several *XDIM_MEMBERSET commands, and then data is

selected from the fact tables.


* XDIM_MEMBERSET TIME = 2011.JAN

* XDIM_MEMBERSET CATEGORY = BUDGET

<Result>

EXTSALES, 2011.JAN, BUDGET, 9000

CASH, 2011.JAN, BUDGET, 3000

TAXES, 2011.JAN, BUDGET, 800

NETINCOME, 2011.JAN, BUDGET, 1500

Now if you add *XDIM_FILTER against the ACCOUNT dimension.

* XDIM_MEMBERSET TIME = 2011.JAN

* XDIM_MEMBERSET CATEGORY = BUDGET

* XDIM_FILTER ACCOUNT = [account].properties(ACCTYPE? = 'AST')

So only one record will be selected from the above result because CASH is the only member of

the account that has the value 'AST' of the ACCTYPE property.

<Result>

CASH, 2011.JAN, BUDGET, 3000

2. Scope using member value

We just figured out how to scope the source data based on the property. So someone could ask

this question. Can we base it on value? For example, can we select all data that has an account

value greater than 100? Of course, we can do it.


The command is *XDIM_GETMEMBERSET . Unlike other command, you need *ENDXDIM

command to specify the data. Here is the grammar of * XDIM_GETMEMBERSET and an

example.

* XDIM_GETMEMBERSET {dimension} [= {member set}]

[*APP = {application}] // optional

[*XDIM_MEMBERSET {dimension} [= {member set}] // as many as needed

[*QUERY_TYPE = 0 | 1 | 2] // optional

* CRITERIA {expression} // required

* ENDXDIM

* XDIM_GETMEMBERSET P_CC = [P_CC]. [H1]. [AAPJ]. CHILDREN

* APP = PLANNING

* XDIM_MEMBERSET P_DataSrc = INPUT

* CRITERIA [P_ACCT]. [H1]. [CE0001000] > 1000

* ENDXDIM

You will get the data which is:

to. Children members of the AAPJ in the P_CC dimension. AND

b. From the PLANNING application

c. Member of the INPUT dimension P_Datasrc AND

d. The member value of CE0001000 of dimension P_ACCT must be greater than 100000

Suppose the fact table has records below it.


CE0001000, 2011.JAN, CURRENT, INPUT, KOREA, 2500

CE0002000, 2011.JAN, CURRENT, INPUT, CHINA, 5000

CE0001000, 2011.JAN, CURRENT, ADJ, CHINA, 3000

CE0002000, 2011.JAN, CURRENT, INPUT, JAPAN, 1999

CE0003000, 2011.JAN, CURRENT, INPUT, JAPAN, 2222

CE0001000, 2011.FEB, BUDGET, ADJ, KOREA, 345

CE0001000, 2011.FEB, BUDGET, INPUT, Türkiye, 1999

CE0003000, 2011.JAN, CURRENT, INPUT, Türkiye, 1100

CE0001000, 2011.FEB, BUDGET, INPUT, CHINA, 1050

CE0001000, 2011.FEB, BUDGET, INPUT, JAPAN, 450

What records will be selected?

The answer is

CE0001000, 2011.JAN, CURRENT, INPUT, KOREA, 2500

CE0001000, 2011.FEB, BUDGET, INPUT, CHINA, 1050

The following records will not be selected even if P_ACCT is CE0001000 because its value is

less than 1000 or Datasrc is not INPUT or is not the secondary member of AAPJ (Asia Pacific)

CE0001000, 2011.JAN, CURRENT, ADJ, CHINA, 3000 (datasrc is not entered)

CE0001000, 2011.FEB, BUDGET, ADJ, KOREA, 345 (datasrc is not entered)

CE0001000, 2011.FEB, BUDGET, INPUT, TURKEY, 1999 (Türkiye is not a member of AAPJ)

CE0001000, 2011.FEB, BUDGET, INPUT, JAPAN, 450 (value is less than 1000)
Here are some important notes for using this command.

Note 1: This command only works for BPC MS.

Note 2 : If you do not specify the scope of each dimension, it will be performed on the

corresponding members of the preselected region that is defined with XDIMMEMBERSET from

the previous line or Passed by Data Manager.

Note 3 – This command will generate an MDX statement so it takes longer to execute. If your

data set only has base members, you can use *XDIM_GETINPUTSET . (Please refer to the

help file)

3. When the user wants to add more


members over the current scope data.

Let's say a user wants to add a USAS sales entity over a predefined set of members. In that

case the user defines it below.

* XDIM_ADDMEMBERSET Entity = USASales The main reason we need this is that sometimes

For example, IN BPC NW, *XDIM_MEMBERSET = BAS (US), CANADA will not work.

Therefore, we must use *XDIM_MEMBERSET and * XDIM_ADDMEMBERSET.


Note: In BPC MS, BAS() will not work with XDIM_MEMBERSET . If the user always wants to

execute a specific set of members every time the logic is executed, they must use

XDIM_ADDMEMBERSET

4. Dynamic scope and saving it in a variable.

Sometimes we need to save our scope data in a script logic variable. But what if your dimension

members are updated frequently? As I know almost all clients update their dimension at least

once a month. If the client changes its dimension members, what will happen in your script

logic? You can use *Filter, but sometimes it may not work all the time.

We can then use the *SELECT and *MEMBERSET commands as a dynamic scoping tool. Like

other scripting engine, Logic script also supports Variable to save some data. The variable is

defined by the % symbol. Here are some examples, %MYTIME%, %CUR%, etc.

So how can we save some data in the variable and when can it be used? Generally, the

variable can be filled using the * SELECT command and the *MEMBERSET command. Both

are scope commands, but *SELECT will be faster because it will create an SQL statement.

Here is the grammar of both commands. *SELECT (, {member set in


MDX format}) Let's see how to use the *SELECT command.

*SELECT (%REPORTING_CURRENCIES%, “ID”, “CURRENCY”, “[GROUP] = 'REP'”)

This command will get the member ID of the currency dimension in which the GROUP property

has the value 'REP'. Actually, it will create a SQL statement like below:
SELECT ID from mbrCurrency where [GROUP] = 'REP'

After the above SQL command is executed, all results will be saved in the variable

%REPORTING_CURRENTIES%.

Here is an example of *MEMBERSET that will do the same result, but will execute the MDX

statement instead of SQL.

*MEMBERSET (%REPORTING_CURRENCIES%, Filter


{[CURRENCY].members, [currency].properties (GROUP = 'REP')})
The variable can be used anywhere in the logic, like in this example:

*XDIM_MEMBER_SET CURRENCY =% REPORTING_CURRENCIES%

Let's assume that the dimension of the coin has below members.

GROUP ID

USD REP

EUR REP

KRW

JPY

Then the above statement will become

*XDIM_MEMBER_SET CURRENCY = USD, EUR

When you define and populate the data with *SELECT and *MEMBERSET , please remember

this as 'MEMBERSET Variable'Note: The MEMBERSET command is only supported in the MS

version.
Example script:

BPC Script logic for Dummies,


chapter 3

Basic writing concept and *REC


declaration
As we saw in my first post, the *REC statement is used to write data.
You should note that *REC will create records based on the scope records.

For example, if your scope record is the same as the following.

<Scope record>

EXTSALES , 2011.JAN, CURRENT , USA, 10000

and your *REC statement is below.


*REC
( FACTOR = 0.9, ACCOUNT = “DISCOUNTED_EXTSALES”,
CATEGORY = “BUDGET” )

Then your generated log will be

<Log generated>
DISCOUNTED_EXTSALES , 2011.JAN, BUDGET , USA, 9000

What if your scope record is not a single record, but multiple records?

<Scope record>

EXTSALES , 2011.JAN, CURRENT , USA. UU, 10000


EXTSALES , 2011.JAN, CURRENT , KOREA, 3000
EXTSALES , 2011.JAN, CURRENT , CANADA, 5000

Then your generated logs will be

<Log generated>
DISCOUNTED_EXTSALES , 2011.JAN, BUDGET , USA. USA, 9000
DISCOUNTED_EXTSALES , 2011.JAN, BUDGET , KOREA, 2700
DISCOUNTED_EXTSALES , 2011.JAN, BUDGET , CANADA, 4500

As you can see, we change the count value, the category value, and the signed
data value (or measurement value) using the *REC statement; the other
dimension not specified in the *REC statement will be the same as the scope
data, so 2011.JAN and each country (entity) will not be changed.

Grammar of the *REC instruction.


Here is the grammar of the *REC statement. You can use FACTOR or
EXPRESSION for various calculations for the signed data value (or
measurement value). And specify the dimension name and member to change
its value.
*REC ( [FACTOR | EXPRESSION] = {Expression} [, {dim1} =
{member}, {dim2} =? )

What is the difference between


FACTOR and EXPRESSION?
The FACTOR is a factor (multiplication) by which the amount recovered is
multiplied. Here is an example:

<Scope record>

EXTSALES, 2011.JAN, CURRENT, 10000

*REC (FACTOR = 6/2)

<Log generated>
EXTSALES, 2011.JAN, CURRENT, 30000

What happens if you want to add or divide ? then you should use the
expression . The EXPRESSION is any formula that will result in the new
value to be published. The formula can include regular arithmetic operators,
fixed values, and the script logic keyword %VALUE%, this represents the
original value retrieved from the scope record. Here is an example:

<Scope record>

EXTSALES, 2011.JAN, CURRENT, 10000

*REC (EXPRESSION =% VALUE% + 5000)

<Log generated>
EXTSALES, 2011.JAN, CURRENT, 15000

Now we have the basics of the *REC statement, but you can ask the
following questions .
“There are some that are data-scoped and need to do different calculations
based on each member of the specific dimension.”
“I need to copy a value to multiple destinations”
“How can I get the value from the other app?”
“I want to use some value from other records to calculate the result”
“Can I use a property value to calculate the result? ”
“The script logic can handle the above requirements”
I'll explain the first question in this post and ask others in the next post.

“There is some scope data and I need to do some calculations based on each
specific dimension member.”
Yes. This is why you MUST use the *REC statement with * WHEN ~ * IS ~
* ELSE ~ * ENDWHEN .

Suppose you want to create salary forecast values and place them in the
forecast category based on the country's actual salary values for January 2011.

We need to increase 10% for the US, 5% for Canada and 3% for other
countries . Let's assume that the ENTITY dimension has country information.
To do this, you need to first scope:
*XDIM_MEMBERSET ACCT = SALARY

*XDIM_MEMBERSET TIME = 2011.JAN


* XDIM_MEMBERSET CATEGORY = CURRENT

Now you need to write the * REC statements

* REC (FACTOR = 1.1 , CATEGORY = “PROGNOSIS”) // 10%


* REC (FACTOR = 1.05 , CATEGORY = “PROGNOSIS”) // 5%
* REC (FACTOR = 1.03 , CATEGORY = “PROGNOSIS”) // 3%

Finally, you must specify a condition for each *REC statement. To do this,
you MUST use the *WHEN~*IS~*ELSE~ENDWHEN statement.
First , write *WHEN and *ENDWHEN outside the *REC statement

*WHEN
* REC (FACTOR = 1.1, CATEGORY = “PROGNOSIS”) // 10%
* REC (FACTOR = 1.05, CATEGORY = “forecast”) // 5%
* REC (FACTOR = 1.03, CATEGORY = “PROGNOSIS”) // 3%

*ENDWHEN

NOTE: It is not necessary to use code indentation in the script logic, but I
would like to recommend using it for better readability.

Second , type a dimension name that you want to compare next to *WHEN .
In this example, it will be dimension ENTITY.

*WHEN ENTITY
* REC (FACTOR = 1.1, CATEGORY = “PROGNOSIS”) // 10%
* REC (FACTOR = 1.05, CATEGORY = “PROGNOSIS”) // 5%
* REC (FACTOR = 1.03, CATEGORY = “PROGNOSIS”) // 3%

*ENDWHEN

Third , place the *IS statement above each *REC statement and the *ELSE
statement above the last *REC statement. We need two statements *IS and
*ELSE because there are two conditions and others will be calculated as one
condition.
*WHEN ENTITY
*ES
*REC (FACTOR = 1.1, CATEGORY = “forecast”) // 10%
*ES
* REC (FACTOR = 1.05, CATEGORY = “forecast”) // 5%

*ELSE
* REC (FACTOR = 1.03, CATEGORY = “PROGNOSIS”) // 3%
* ENDWHEN

Fourth, put each condition value next to *IS

*WHEN ENTITY
*IS USA
*REC (FACTOR = 1.1, CATEGORY = “forecast”) // 10%
*IS CANADA
*REC (FACTOR = 1.05, CATEGORY = “PROGNOSIS”) // 5%
*ELSE
*REC (FACTOR = 1.03, CATEGORY = “PROGNOSIS”) // 3%

*ENDWHEN

Fourth , put * COMMIT at the end of the script so that the logic engine can
post data to the database.

so the final version should be the same as the code below.


*XDIM_MEMBERSET ACCT = SALARY

*XDIM_MEMBERSET TIME = 2011.JAN


*XDIM_MEMBERSET CATEGORY = CURRENT

*WHEN ENTITY
*IS USA
*REC (FACTOR = 1.1, CATEGORY = “forecast”) // 10%
*IS CANADA
*REC (FACTOR = 1.05, CATEGORY = “PROGNOSIS”) // 5%
*ELSE
*REC (FACTOR = 1.03, CATEGORY = “PROGNOSIS”) // 3%
*ENDWHEN
*COMMIT

Note 1: You can use multiple condition value like *IS VALUE_A, VALUE_B
Note 2: You can use >, <= with numeric value with the *IS statement,
example *IS > 4; defaults to equals (=), so it will be fine, even if you don't
specify it.
Note 3: AND, OR and NOT cannot be used with *IS
Note 4: (double quote) is not required to compare the string value with the
*IS statement.
Note 5: * The WHEN statement can be nested. For example,

*WHEN xxx
*IS “A”
*REC (…)
*REC (…)
*IS “B”
*REC (…)
*WHEN yyy
*IS “C”, “D”, “E”
*REC (…)
*ELSE
*REC (…)
*ENDWHEN
*ENDWHEN

Note 6: You can use the value of a property with the *IS statement. Example:
*IS Intco.Entity

BPC Script logic for Dummies,


chapter 4
How can I get the value from the other application?

The simple answer is... USE LOOKUP / ENDLOOKUP!

The simplest example is currency conversion because you need to read the rate value from the

rate app to convert

finance app currency values.

NOTE: *LOOKUP / *ENDLOOKUP can also be used to read the value of the current

application.

Here is the syntax of *LOOKUP / *ENDLOOKUP

The syntax is:

*LOOKUP {Application}
*DIM [{LookupID}:] {DimName} = “Value” | {CallingDimensionName}[.
{Property}]
[*DIM …]
*ENDLOOKUP
{Application} is the name of the application that will retrieve the value.

{DimensionName} is a dimension in the search application.

{CallingDimensionName} is a dimension in the current application.

{LookupID} is an optional variable that will hold the value so you can use it
in the script. This is only necessary when multiple values must be retrieved.

Now, let's do it step by step.


Here are our requirements for currency conversion.

1. You need to get the rate values from the rates app for currency conversion (LC to USD and

EUR).

2. The member ID of the RATE dimension in the rate application must be the same as the

RATETYPE property of the account dimension in the finance application.

3. The member ID of the RATEENTITY dimension in the rate application must be “DEFAULT”

4. The currency conversion rule is 'DESTINATION CURRENCY / CURRENT CURRENCY'

First, you need to define *LOOKUP with the name of the application.
*LOOKUP RATE
* ENDLOOKUP

Second, specify the dimensions of the RATE application with the *DIM statement.

(Suppose the rate application has the dimension RATEENTITY, INPUTCURRENCY, RATE,

CATEGORY and TIME.)

*LOOKUP RATE

*DIM RATEENTITY

*DIM INPUTCURRENCY

*DIM RATE

*DIM CATEGORY

*DIM TIME

* ENDLOOKUP

Third, assign the member ID value of each dimension from the current
application (Finance) or use a fixed value. If you need to retrieve multiple
values according to different member ID values of specific dimensions, make
copies of that dimension and assign different values.
*LOOKUP RATE
*DIM RATEENTITY = “DEFAULT” // Fixed value
*DIM INPUTCURRENCY = “USD” // Fixed value
*DIM INPUTCURRENCY = “EUR” // Fixed value, copy the same
dimension for another value
*DIM INPUTCURRENCY = ENTITY.CURR // added one more for
currency conversion as variable value
*DIM RATE = ACCOUNT.RATETYPE // Variable value based on current
application
*DIM CATEGORY
*DIM TIME
*ENDLOOKUP
Fourth, place variables for multiple recall values in front of each duplicate dimension name.

*LOOKUP RATE

*DIM RATEENTITY = “DEFAULT”

*DIM DESTCURR1: INPUTCURRENCY = “USD”

*DIM DESTCURR2: INPUTCURRENCY = “EUR”

*DIM SOURCECUR: INPUTCURRENCY = ENTITY.CURR

*DIM RATE = ACCOUNT.RATETYPE

*DIM TIME

* ENDLOOKUP

Note: If you want to get some value based on two or more dimensions, you must use the

same variable name when Map Dimensions. Here is an example.

*LOOKUP OWNERSHIP

*DIM INTCO = “IC_NONE”


*DIM PARENT = “MYPARENT”

*DIM PCON: ACCOUNTOWN = “PCON” // PCON is used for ACCOUNTOWN

*DIM PCON: ENTITY = ENTITY // PCON is used for ENTITY

*DIM IC_PCON: ACCOUNTOWN = “PCON” // IC_PCON is used even if it searches for

the same “PCON”

*DIM IC_PCON: ENTITY = INTCO.ENTITY // IC_PCON is used for INTCO.ENTITY

*ENDLOOKUP

Although the member ID of the ACCOUNTOWN dimension is the same, the variable must

be defined as a different variable because the member ID of the ENTITY dimension is different

in the join. If the 'ENTITY' property of the INTCO dimension has a value I_FRANCE, *LOOKUP

will then select two records and each variable will have a different value.

IC_NONE, MYPARENT, PCON, FRANCE, 100 => PCON

IC_NONE, MYPARENT, PCON, I_FRANCE, 80 => IC_PCON

Lastly, remove dimension names (TIME and CATEGORY> that do not have any fixed value

or variable value because it will be passed as current value automatically.

*LOOKUP RATE
*DIM RATEENTITY = “DEFAULT”
*DIM SOURCECUR: INPUTCURRENCY = ENTITY.CURR
*DIM DESTCURR1: INPUTCURRENCY = “USD”
*DIM DESTCURR2: INPUTCURRENCY = “EUR”
*DIM RATE = ACCOUNT
*ENDLOOKUP
So how can we use these values? You can use it by using
LOOKUP(Variable) in your *REC declaration as shown below
* WHEN ACCOUNT.RATETYPE

*IS “AVG”, “END”

*REC (FACTOR = LOOKUP (DESTCURR1) / LOOKUP (SOURCECURR), CURRENCY =

“USD”)

*REC (FACTOR = LOOKUP (DESTCURR2) / LOOKUP (SOURCECURR) , CURRENCY =

“EUR”)

*ENDWHEN

NOTE: You can use LOOKUP (variable) with *WHEN and *IS statement

Ex) *WEHN LOOKUP (PCON) // as a when condition value

*IS <= LOOKUP (IC_PCON) // as a value of IS

*REC (FACTOR = -1, PARENT = “MYPARENT”, DATASRC = “ELIM” )

*ENDWHEN

We review how to define the *LOOKUP / *ENDLOOKUP statement and how to use it.

Now it's time to find out how it works in the scripting engine.

Let's assume the following logs are in the velocity application and see what will happen during

the execution of the script logic.

RATEENTITY, INPUTCURRENCY, RATE, CATEGORY, TIME, SIGNED DATA

DEFAULT, USD, AVG, CURRENT, 2011.JAN, 1

DEFAULT, EUR, AVG, CURRENT, 2011.JAN, 1.22


DEFAULT, CHF, AVG, CURRENT, 2011.JAN, 0.91

DEFAULT, USD, END, CURRENT, 2011.JAN, 1

DEFAULT, EUR, END, CURRENT, 2011.JAN, 1.24

DEFAULT, CHF, FIN, CURRENT, 2011.JAN, 0.93

RATCALC, USD, AVG, CURRENT, 2011.JAN, 1

RATCALC, USD, AVG, CURRENT, 2011 .JAN, 1

Here are your current funding request records that need to be processed.

ACCOUNT, ENTITY, CATEGORY, TIME, CURRENCY,

INVENTORY, SWITZERLAND, CURRENT, 2011.JAN, LC, 5000

INCOME, SWITZERLAND, CURRENT, 2011.JAN, LC, 1000

As you can see, there is no relationship between the finance app and the rates app. We know

that the currency of Switzerland is CHF, but there is no information in each record of the fact

table. It only has LC value (local currency).

So how can the script logic find the value and calculate it?

The key point is 'ENTITY.CURR', which we use to assign the dimension as shown below.

*DIM SOURCECUR: INPUTCURRENCY = ENTITY.CURR

ENTITY.CURR means the 'CURR' property value of the ENTITY dimension. Therefore,

Switzerland, which is one of the members of the Entity dimension, must have the value 'CHF' in

its 'CURR' property.


The same is for mapping the RATE dimension of the rate application as shown below.

*DIM RATE = ACCOUNT.RATETYPE

Therefore, the value of the 'RATETYPE' property of the INVENTORY and REVENUE account

must have the value 'AVG' or 'END'.

Therefore, the Script engine will perform the following steps to process the first record in the fact

table.

1. Select RATEENTITY = “DEFAULT”

2. Select INPUTCURRENCY = “ CHF ” ( because the current value of the entity member's

'CURR' property is 'CHF' )

OR INPUTCURRENCY = “USD”

OR INPUTCURRENCY = “EUR”

3. Select RATE = “ END ” ( because the value of the checking account member's

'RATETYPE' property is 'END' )

4. Select CATEGORY = “CURRENT” (There is no declaration, so it is the same as the

CATEGORY value of the current application).

5. Select TIME = “2011.JAN” (there is no declaration, so it is the same as the TIME value of the

current application).

All previous selection will be combined with 'AND' condition.


Therefore, the 3 records below will be selected and their signed data value will be assigned to

each variable.

DEFAULT, USD, END, CURRENT, 2011.JAN, 1 => DESTCURR1 will be 1

DEFAULT, EUR, END, CURRENT, 2011.JAN, 1.24 => DESTCURR2 will be 1.24

DEFAULT, CHF, END, CURRENT, 2011.JAN, 0.93 => SOURCECUR will be 0.93

After the script logic engine executes the following statements, it will generate 2 records.

* WHEN ACCOUNT.RATETYPE

*IS “AVG”, “END”

* REC (FACTOR = LOOKUP (DESTCURR1) / LOOKUP (SOURCECURR), CURRENCY =

“USD”)

* REC (FACTOR = LOOKUP (DESTCURR2) / LOOKUP (SOURCECURR) , CURRENCY =

“EUR”)

*ENDWHEN

ACCOUNT, ENTITY, CATEGORY, TIME, CURRENCY,

INVENTORY, SWITZERLAND, CURRENT, 2011.JAN, LC, 5000

INVENTORY, SWITZERLAND, CURRENT, 2011.JAN, USD, 5376.34 // 5000 * (1 / 0.93)

INVENTORY, SWITZERLAND, CURRENT, 2011.JAN, EUR, 6666.67 // 5000 * (1.24 / 0.93)

For the second record in the fact table, the 3 records below will be selected from the rate

application fact table because

The revenue account has “AVG” RATETYPE.


DEFAULT, USD, AVG, CURRENT, 2011.JAN, 1

DEFAULT, EUR, AVG, CURRENT, 2011.JAN, 1.22

DEFAULT, CHF, AVG, CURRENT, 2011.JAN, 0.91

After you process the 'REVENUE' records, there will be 6 records in the fact table as shown

below.

(4 records will be generated in total).

ACCOUNT, ENTITY, CATEGORY, TIME, CURRENCY,

SIGNED DATA INVENTORY, SWITZERLAND, CURRENT, 2011.JAN, LC, 5000

INVENTORY, SWITZERLAND, CURRENT, 2011.JAN, USD, 5376.34

INVENTORY, SWITZERLAND, CURRENT, 2011.JAN, EUR, 6666.67

INCOME, SWITZERLAND, CURRENT, 2011.JAN, LC, 1000

INCOME, SWITZERLAND, CURRENT, 2011.JAN, USD, 1098.90 // 1000 * (1 / 0.91)

INCOME, SWITZERLAND, CURRENT, 2011.JAN, EUR, 1340.66 // 1000 * (1.22 / 0.91)

We finished learning how to use the *LOOKUP / *ENDLOOKUP instruction.

Here are some questions and answers I received frequently.

Question 1: What if the rate application does not have the value?

Then the currency conversion will not happen.

Question 2: I don't have any records in the current application's fact table. What will happen?

The script logic always reads logs from the current application. Therefore, if there are no

records in the current application's fact table, nothing will happen.


Question 3: Can we look up the value of the parent member instead of the base member (leaf

member)?

The MS version can do it with the *OLAPLOOKUP statement instead of *LOOKUP, but the NW

version doesn't have it yet.

BPC Script logic for Dummies,


chapter 5

Like other program or script language, logic script also supports LOOP
statement. Let's see how it works.
Here is the syntax of the *FOR – *NEXT statement.
*FOR {variable1} = {set1} [AND {variable2 = {set2}]
{another statement...}

*NEXT
And here is an example.

*FOR % CURR% = USD, EURO


*REC (FACTOR = 1.1, TIME = 2011.OCT, CURRENCY = % CURR%)
*NEXT
So what is the meaning of the above example?

1. We set a variable like %CURR%

2. The variable % CURR% will be replaced by two values USD and EURO.

3. The *REC statement includes the variable %CURR%.


4. Therefore, two statements will be translated like below

because the *REC statement exists inside the *FOR – *NEXT statement.
*REC (FACTOR = 1.1, TIME = 2011.OCT, CURRENCY = USD)
*REC (FACTOR = 1.1, TIME = 2011.OCT, CURRENCY = EURO)
Suppose the variable %CURR% has USD, EURO, CHF, KRW and then it
will be translated as follows.

*REC (FACTOR = 1.1, TIME = 2011.OCT, CURRENCY = USD)


*REC (FACTOR = 1.1, TIME = 2011.OCT, CURRENCY = EURO)
*REC (FACTOR = 1.1, TIME = 2011.OCT, CURRENCY = CHF)
*REC (FACTOR = 1.1, TIME = 2011.OCT, CURRENCY = KRW)
Someone may say “we can use several lines of the *REC statement”.
Of course, it is fine if it is simple, but we need the *FOR – *NEXT statement
because it can be used as a nested form.
For example,

*FOR %YEAR% = 2003,2004,2005


*FOR %MONTH% = JAN, FEB, MAR, APR, MAY, JUNE, JULY,
AUGUST, SEP, OCT, NOV, DEC
*REC (FACTOR = GET(ACCOUNT = ” TOT.OVRHEAD “, TIME =”
TOT.INP “) / 100, TIME =”% YEAR%.% MONTH% “)
*NEXT

*NEXT

If the user is using the *REC statement, the user must write 36 statements
instead of a simple *FOR – *NEXT statement above.
(NOTE: BPC NW supports a nested FOR – NEXT in version 7.5)

Additionally, the user can use two sets of variables like the example below.

*FOR %X% = 1,2,3 AND %Y% = A, B, C


*REC (FACTOR = 1.5, TIME = %X%, CURRENCY = %Y%)
*NEXT
So the first set of variables and the second set of variables will be combined 1
to 1; then it will be replaced like the following example.

*REC (FACTOR = 1.5, TIME = 1, CURRENCY = A)


*REC (FACTOR = 1.5, TIME = 2, CURRENCY = B)
*REC (FACTOR = 1.5, TIME = 3, CURRENCY = C)
What happens if the number of values does not match between the first and
second variables?

If the first variable has fewer values than the second variable,

additional values of the second variable will be ignored.

If the first variable has more values than the second variable,

missing values of the second variable will be considered null, so be careful to


match the variable number.
The last thing about *FOR – *NEXT is to use the data set variable as values.
Users can use data_set as %TIME_SET% instead of specifying all time
members.

This is very useful when we use script logic with dynamic data set.

For example, we can use

*FOR %MYTIME% = %TIME_SET%


instead of

*FOR %MYTIME% = 2003.JAN, 2004.JAN, 2005.JAN


Therefore, users can execute the script logic dynamically based on the passed
data sets.

You might also like