In this section we will show four examples of logics that calculate different types of rebates:
- Tiered rebate – based on quantities or amounts purchased.
- Stepped rebate – tiered rebate where the rebate is calculated separately for each tier.
- Growth rebate – based on a specified increase in purchases.
- Fixed rebate – not dependent on the purchased amounts.
See also the RebateManager Calculation Logic Examples Rebate Variants section below for detailed explanation of the rebate types.
Tiered Rebate
The complete calculation logic will have the following elements:
Type | Element Name | Display Mode | Format Type |
---|
Image Modified | Library | Never |
|
Image Modified | RetainGlobal | Never |
|
Image Modified | CustomerGroup | Never |
|
Image Modified | ProductGroup | Never |
|
Image Modified | SalesTarget | Never |
|
Image Modified | ActualsQuery | Never |
|
Image Modified | ActualSales | Never |
|
Image Modified | TotalSalesRebate | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | ASFFactor | Never |
|
Image Modified | SalesForecast | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | RebateForecast | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | RebateRecords | Never |
|
...
RetainGlobal – The usage of global variables must be explicitly enabled.
From 3.8 Manhattan, api.retainGlobal
can be set to true
by default in /wiki/spaces/DEV/pages/99575741 General Settings.
Code Block |
---|
language | groovy |
---|
title | RetainGlobal |
---|
|
api.retainGlobal=true |
...
Code Block |
---|
language | groovy |
---|
title | SalesTarget |
---|
|
def tieredVal = api.multiTierEntry("Revenue Target", "€","%")
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
// 5% --> 0.05 conversion
if (tieredVal != null){
tieredVal = tieredVal.multiplyValues(0.01)
}
api.log("sales target", tieredVal.inspect())
api.global.target = tieredVal
return tieredVal |
...
Code Block |
---|
language | groovy |
---|
title | ActualsQuery |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return def line = api.currentItem()
if //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def line = api.currentItem()
if (line != null){ // is null on syntaxinput checkgeneration/formula save
def customerGroup = api.getElement("CustomerGroup")
def productGroup = api.getElement("ProductGroup")
// actuals for the period specified in the rebate line item
def timeFilter = api.filter("InvoiceDate", line?.startDate, line?.endDate)
if (timeFilter != null){
api.datamartQuery("TX", "Transaction DM", customerGroup, productGroup, timeFilter, "InvoicePrice\$")
}
} |
...
Code Block |
---|
language | groovy |
---|
title | ActualSales |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.datamartLookup("TX", "InvoicePrice\$")
api.log("actual", sales.inspect())
api.global.actual = sales as BigDecimal
return api.global.actual |
...
Code Block |
---|
language | groovy |
---|
title | TotalSalesRebate |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return def sales = api.global.actual
if (sales<0) //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.global.actual
if (sales<0) sales=0
def rebate = Library.calcDiscount(sales, api.global.target)
api.global.rebate = rebate as BigDecimal
return api.global.rebate |
...
Code Block |
---|
language | groovy |
---|
title | RebateForecast |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.global.actual * api.getElement("ASFFactor")
if (sales<0) sales=0
def rebate = Library.calcDiscount(sales, api.global.target)
api.global.rebate = rebate as BigDecimal
return api.global.rebate |
...
Code Block |
---|
language | groovy |
---|
title | RebateRecords |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
rebateRecords.add() |
Stepped Rebate
The complete calculation logic will have the following elements:
Type | Element Name | Display Mode | Format Type |
---|
Image Modified | Library | Never |
|
Image Modified | RetainGlobal | Never |
|
Image Modified | CustomerGroup | Never |
|
Image Modified | ProductGroup | Never |
|
Image Modified | SalesTarget | Never |
|
Image Modified | ActualsQuery | Never |
|
Image Modified | ActualSales | Never |
|
Image Modified | TotalSalesRebate | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | ASFFactor | Never |
|
| SalesForecast | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | RebateForecast | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | RebateRecords | Never |
|
...
Code Block |
---|
language | groovy |
---|
title | Library |
---|
|
//Stepped discount calculation function: revenue falls in a specific tier, %discount of each tier up to this tier is applied to the part of the revenue.
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
// target values are % values (i.e. 5% is represented by 5 rather than 0.05)
def calcSteppedDiscount(actual, tieredTarget){
api.log("calcSteppedDiscount: actual", actual.inspect())
api.log("calcSteppedDiscount: target", tieredTarget.inspect())
def cumulativeDiscount = 0.0
if (actual != null && tieredTarget != null){
if (tieredTarget.size()>0){
def unusedActual = actual
for (i = tieredTarget.size()-1; i>=0; i--){
def tier = tieredTarget.get(i)
def target = tier.target as BigDecimal
def value = tier.value as BigDecimal
api.log("Tier" +i + " target", target.inspect())
api.log("Tier" +i + " value", value.inspect())
if (target != null){
if (unusedActual > target){
if (value == null) value=0
def discountable = unusedActual - target
def discount = discountable * value
api.log("Discountable", discountable)
api.log("Discount", discount)
cumulativeDiscount += (discountable * value)
api.log(" Cumulative discount", cumulativeDiscount)
unusedActual = target
}
}
}
}
}
return cumulativeDiscount
}
|
RetainGlobal – The usage of global variables must be explicitly enabled.
From 3.8 Manhattan, api.retainGlobal
can be set to true
by default in /wiki/spaces/DEV/pages/99575741 General Settings.
Code Block |
---|
language | groovy |
---|
title | RetainGlobal |
---|
|
api.retainGlobal=true |
...
Code Block |
---|
language | groovy |
---|
title | SalesTarget |
---|
|
def tieredVal = api.multiTierEntry("Revenue Target", "€","%")
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
// 5% --> 0.05 conversion
if (tieredVal != null){
tieredVal = tieredVal.multiplyValues(0.01)
}
api.log("sales target", tieredVal.inspect())
api.global.target = tieredVal
return tieredVal |
...
Code Block |
---|
language | groovy |
---|
title | ActualsQuery |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def line = api.currentItem()
if (line != null){ // is null on syntaxinput checkgeneration/formula save
def customerGroup = api.getElement("CustomerGroup")
def productGroup = api.getElement("ProductGroup")
// actuals for the period specified in the rebate line item
def timeFilter = api.filter("InvoiceDate", line?.startDate, line?.endDate)
if (timeFilter != null){
api.datamartQuery("TX", "Transaction DM", customerGroup, productGroup, timeFilter, "InvoicePrice\$")
}
} |
...
Code Block |
---|
language | groovy |
---|
title | ActualSales |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.datamartLookup("TX", "InvoicePrice\$")
api.log("actual", sales.inspect())
api.global.actual = sales as BigDecimal
return api.global.actual |
...
Code Block |
---|
language | groovy |
---|
title | TotalSalesRebate |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.global.actual
if (sales<0) sales=0
def rebate = Library.calcSteppedDiscount(sales, api.global.target)
api.global.rebate = rebate as BigDecimal
return api.global.rebate |
...
Code Block |
---|
language | groovy |
---|
title | RebateForecast |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.global.actual * api.getElement("ASFFactor")
if (sales<0) sales=0
def rebate = Library.calcDiscount(sales, api.global.target)
api.global.rebate = rebate as BigDecimal
return api.global.rebate |
...
Code Block |
---|
language | groovy |
---|
title | RebateRecords |
---|
| if (api.isSyntaxCheck()) return |
if (api.isInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
rebateRecords.add() |
Growth Rebate
The complete calculation logic will have the following elements:
Type | Element Name | Display Mode | Format Type |
---|
Image Modified | Library | Never |
|
Image Modified | RetainGlobal | Never |
|
Image Modified | CustomerGroup | Never |
|
Image Modified | ProductGroup | Never |
|
Image Modified | GrowthTarget | Never |
|
Image Modified | ActualsQuery | Never |
|
Image Modified | ActualSales | Never |
|
Image Modified | PreviousYearSales | Never |
|
Image Modified | GrowthRebate | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | ASFFactor | Never |
|
Image Modified | SalesForecast | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | RebateForecast | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | RebateRecords | Never |
|
...
RetainGlobal – The usage of global variables must be explicitly enabled.
From 3.8 Manhattan, api.retainGlobal
can be set to true
by default in /wiki/spaces/DEV/pages/99575741 General Settings.
Code Block |
---|
language | groovy |
---|
title | RetainGlobal |
---|
|
api.retainGlobal=true |
...
Code Block |
---|
language | groovy |
---|
title | GrowthTarget |
---|
|
def tieredVal = api.multiTierEntry("Growth Target", "%","%")
if (api.isSyntaxCheck()) return "%","%")
if (api.isInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
//rebate % conversion
if (tieredVal != null){
tieredVal = tieredVal.multiplyValues(0.01)
}
api.log("growth target", tieredVal.inspect())
api.global.target = tieredVal
return tieredVal |
...
Code Block |
---|
language | groovy |
---|
title | ActualsQuery |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def line = api.currentItem()
if (line != null){ // is null on syntaxinput checkgeneration/formula save
def customerGroup = api.getElement("CustomerGroup")
def productGroup = api.getElement("ProductGroup")
// actuals for the period specified in the rebate line item
def timeFilter = api.filter("InvoiceDate", line?.startDate, line?.endDate)
if (timeFilter != null){
api.datamartQuery("TX", "Transaction DM", customerGroup, productGroup, timeFilter, "InvoicePrice\$")
}
// previous year (copy and paste for other past periods)
def cal = api.datamartCalendar()
def startDate = line?.startDate?.toString() // is a string
def endDate = line.endDate.toString() // is a string
if (startDate != null){
startDate = cal.add(startDate, -1, "YEAR");
}
if (endDate != null){
endDate = cal.add(endDate, -1, "YEAR");
}
timeFilter = api.filter("InvoiceDate", startDate, endDate)
if (timeFilter != null){
api.datamartQuery("TX-1yr", "Transaction DM", customerGroup, productGroup, timeFilter, "InvoicePrice\$")
}
} |
...
Code Block |
---|
language | groovy |
---|
title | ActualSales |
---|
|
if (api.isSyntaxCheck()) returnisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.datamartLookup("TX", "InvoicePrice\$")
api.log("actual", sales.inspect())
api.global.actual = sales as BigDecimal
return api.global.actual |
...
Code Block |
---|
language | groovy |
---|
title | PrevYearSales |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.datamartLookup("TX-1yr", "InvoicePrice\$")
api.log("actual", sales.inspect())
api.global.actualPrevYear = sales as BigDecimal
return api.global.actualPrevYear |
...
Code Block |
---|
language | groovy |
---|
title | GrowthRebate |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.global.actual
def lastYearsSales = api.global.actualPrevYear
if (sales<0) sales=0
if (lastYearsSales<0) lastYearsSales=0
def rebate = Library.growthBonus(sales, lastYearsSales, api.global.target)
api.global.rebate = rebate as BigDecimal
return api.global.rebate |
...
Code Block |
---|
language | groovy |
---|
title | RebateForecast |
---|
|
if (api.isSyntaxCheck()) return
(api.isInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
def sales = api.global.actual * api.getElement("ASFFactor")
if (sales<0) sales=0
def rebate = Library.calcDiscount(sales, api.global.target)
api.global.rebate = rebate as BigDecimal
return api.global.rebate |
...
Code Block |
---|
language | groovy |
---|
title | RebateRecords |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
rebateRecords.add() |
Fixed Rebate
The complete calculation logic will have the following elements:
Type | Element Name | Display Mode | Format Type |
---|
Image Modified | CustomerGroup | Never |
|
Image Modified | ProductGroup | Never |
|
| FixedRebate | QuoteConfiguratorQuoting | Money (EUR) |
Image Modified | RebateRecords | Never |
|
...
Code Block |
---|
language | groovy |
---|
title | RebateRecords |
---|
|
if (api.isSyntaxCheckisInputGenerationExecution()) return //isInputGenerationExecution supported from version 10.0, in older versions use isSyntaxCheck
rebateRecords.add() |
Rebate Variants
The following types are only examples, in Pricefx you can set up any rebate type Condition Type you need.
Tiered Rebate
This is the simplest rebate type that is based on quantities or amounts purchased by the customer. The tiered rebate type defines several tiers with corresponding rebate percentages. The paid out rebate depends on the highest threshold level that has been reached.
...
Threshold | Rebate Percentage | Threshold Reached | Calculation | Bonus |
---|
$10,000 | 1.0% | Yes |
|
|
$15,000 | 1.5% | Yes | 17,200 * 0.015 | $258 |
$20,000 | 2.0% | No |
|
|
Growth Rebate
The growth rebate type is based on a specified increase in purchases. Again, several tiers are defined and the paid out rebate depends on the threshold that has been reached.
...
Threshold | Rebate Percentage | Threshold Reached | Calculation | Bonus |
---|
10% | 1.0% | Yes |
|
|
20% | 1.5% | Yes | 17,200 * 0.015 | $258 |
30% | 2.0% | No |
|
|
Stepped Rebate
The bonus is paid for each tier whose lower threshold has been exceeded but only from the value that is within the tier limits and the percentage specified for that tier is applied.
...