Lab Info
Lesson | Payout Records |
Category / Topic / Section | Pricefx Core / Payout Records / CFG 2 |
Target Audience | Certified Configuration Engineer |
Estimated Time to complete | 0:45 |
Learning Outcomes
At the end of the Lab, you should be able to:
implement creation of PayoutRecords
configure Payout Record metadata
Figure 1. Sneakpeek of the generated Payout Records per CustomerId
Pre-requisites
In order to be able to complete this laboratory exercise, please complete the prerequisites before you proceed.
Labs
This lab expects, that you already have in your partition a solution of the preceding labs:
Rebates Configuration - Lab 2
If you need to work on this lab without finishing the preceding ones first, you can deploy the solutions of the pre-requisite labs provided in a ZIP file attached to it.
User Story / Requirements
As a Sales Manager, I want to calculate the rebates on a SoldTo(CustomerId) level, so that I have fine-grained rebates ready as soon as we approve the rebate amount (in Rebate Records).
Details
What should be calculated:
Rebate Record - will calculate the summary of the rebate on the level entered by the user (likely some set of customers)
Payout Record - will calculate the rebate amount for each SoldTo(CustomerId). The rebate amount for particular customer will be proportional to the rebate amount of the group, based on the sizes of the Actual Sales.
Acceptance Criteria
when the user recalculates the Rebate Record, the Payout Records will be created (or updated, if they already existed)
the Payout Record has fields and values:
type - "Payout"
name - combination of Rebate Record ID and Customer ID
startDate, endDate, payoutDate - the same as the ones used for the Rebate Record
Customer Id - so we can easily distinguish for which customer is the amount
Actual Rebate - the amount of rebate for the particular customer
Currency - in which currency is the rebate amount
Rebate % - this is the percentual amount of rebate - the same number as on the Rebate Record
Implementation Steps
Step: Create the new Rebate logic and Condition Type
We will utilize the logic from pre-requisite Lab and build on top of it.
Prepare the new Rebate Logic:
In Studio, make a copy of the BonusOnSales logic
edit the file logic.json of this new logic and change of this copy to "BonusOnSalesWithPayoutRecords". This action should not only change the name of logic inside of the Json file, but also the name of the folder with logic.
Deploy the logic to your partition
prepare a new Rebate Type to use this logic:
In Pricefx UI, navigate to Rebates Condition Types
create a new Condition Type "BonusOnSalesWithPayoutRecords" which uses as Pricing Logic the new logic BonusOnSalesWithPayoutRecords
Step: Prepare testing data
To be able to test, we need at least one Rebate Agreement with one line, which will generate at least one Rebate Record.
create new Rebate Agreement, for example:
For header
Description: "Agreement with Payout Records"
Start Date: 2021-01-01
End Date: 2021-12-31
Calculation Date: 2021-05-31
Payout Date: 2022-04-01
Items - add new rebate item of the new type BonusOnSalesWithPayoutRecords you just created
Customer(s): APPO AG (Customer Group)
Product(s): Meatball (Business Unit)
Rebate %: 1
Recalculate the Rebate Agreement
Save the Rebate Agreement
Find the Rebate Record which was generated from this Agreement and note down its ID, you will need it later.
Step: Add the code to generate the Payout Records
Now you will modify this newly added Logic to also generate the Payout Records.
In Studio, open your logic BonusOnSalesWithPayoutRecords
Add the code, which queries Actual Sales per Customer
Add a new Element "ActualSalesPerCustomer" right before the element "ActualSales". The code will be:
def dmCtx = api.getDatamartContext() def dmTable = dmCtx.getDatamart("Transaction") def query = dmCtx.newQuery(dmTable, true) .select("CustomerId", "CustomerId") .select("SUM(InvoicePrice)", "TotalRevenue") .where(out.CustomerGroup) .where(out.ProductGroup) .where( Filter.greaterOrEqual("InvoiceDate", out.StartDate), Filter.lessOrEqual("InvoiceDate", out.EndDateOrToday) ) def result = dmCtx.executeQuery(query) return result?.data?.collectEntries { row -> [(row.CustomerId): row.TotalRevenue] }
use Roll Up / Group Bygroup Actual Sales by CustomerIdcreate a Map with data: CustomerId → ActualSales
This Element ActualSalesPerCustomer will be used in all contexts, so either keep Calculation Contexts Agreement, Agreement ReadOnly and RebateRecord all blanks or all crossed
to avoid querying the data 2x for the same reason, modify the code of existing element ActualSales, so that it only summarizes the data found in element ActualSalesPerCustomer. So the code of ActualSales element will be:
return (out.ActualSalesByCustomer as Map)?.values()?.sum()
Add the code, which generates the Payout Records:
Add new Element "CreatePayoutRecords" to the end of the logic, with code:
if (out.RebatePct == null) return def actualSales = out.ActualSalesByCustomer as Map def customerIds = actualSales.keySet() def rebateRecord = api.currentItem() payoutRecords.bulkLoadMode() customerIds.each { customerId -> def rebateValue = (actualSales[customerId] ?: 0.0) * out.RebatePct def payoutRecord = [ startDate : rebateRecord.startDate, endDate : rebateRecord.endDate, payoutDate: rebateRecord.payoutDate, attribute1: customerId, attribute2: rebateValue, attribute3: "EUR", attribute4: out.RebatePct, ] payoutRecords.add( "Payout", "${rebateRecord.uniqueName}-${customerId}" as String, payoutRecord ) } payoutRecords.flush()
start the Bulk Mode, which helps with speed when generating many thousands of PayoutRecords. If you generate lower amounts, you might not use it and simply commenting out this line should keep the functionality.create the new Payout Record. If the record already existed, it will be updated.use flush() to ensure, that your new records are fully stored in tables. This is a must have for bulk-mode, but not needed for non-bulk mode. Since it’s ok to use it also in non-bulk mode, you can simply use it all the times, once you’re done with generation of the Payout Records.
This Element "CreatePayoutRecords" is used ONLY when recalculating Rebate Records, so ensure, that only the Calculation Context "RebateRecord" is crossed
Test the Logic in Studio
in Studio you must test, if your logic is executed without failure, but you cannot test the "writing" of the data to the tables - the writing operation works only in non-testing execution.
setup the input parameters
Context - ensure to select REBATERECORD
RebateRecordName - type a name of existing Rebate Record - e.g. the one you created for testing purposes at the beginning.
startDate, endDate - choose a timeframe, where you know you have data in your Datamart. You can use the same you used while creating the Agreement.
calculation Date - will be used as Target Date. In the code, the Target Date is used to find the period of time for which we search the data.
targetDate - not used, the value does not matter, because it will be overriden by calculationDate
as Studio has the possibility to save the parameters, we strongly encourage you to use it, so that you will not loose the values, when you close the Logic or Studio. In the screenshot you can notice the Load Preset Test RR.
Verify, if the logic passed ok, and the values of the ActualSalesByCustomer, if you got realistis numbers.
you can also add
api.trace()
to element CreatePayoutRecords to visualize, if the PayoutRecords are asked to be created.Remember, they will not be physically created in the Test mode.
Test the logic in Pricefx UI
You will test it on the Rebate Record detail page. So navigate to Rebates Rebate Records
click on your Rebate Record’s ID (it’s a link), to open its detail page
click on Calculate to recalculate the Rebate Record - since the same logic also creates the Payout Records, they will be created
you do not need to Save the calculated result to "save" the Payout Records. They’re created already during the execution of the Logic.
Please note, that the Save cause Calculation and only then saves. So if the users would use manually this detail page for recalculations, they must be trained to properly understand, how buttons Calculate and Save work.
Remember, that the Rebate Records are typically calculated by the Rebate Calculation job, where the logic executes ones (and stores the Payout Records to database and prepares the values for Rebate Record), and then the Rebate Record values are persisted too.
review the PayoutRecords list - navigate to Rebates Payout Records and review the data in the columns
verify, that you have more customers and they have different rebates. Of course, this depends on the data set, date-ranges, etc., so validate it with respect to your dataset.
Step: Customize the columns of Payout Records table
You’re almost done, the last thing is to modify the names of the columns and how their values are formatted on the screen for the end-user.
Rename and Customize columns of the Payout Records
in Pricefx UI, navigate to Rebates Payout Records
for columns attribute1..4 use Rename and Customize columns to set up properties of the columns
AttributeNameLabelTypeFormat Type
attribute1
CustomerId
Customer Id
String
-
attribute2
ActualRebate
Actual Rebate
Real
Money
attribute3
Currency
Currency
String
-
attribute4
RebatePct
Rebate %
Real
Percent
fetch definition into project
in Studio, fetch the object Payout record attr (PYR), PayoutRecordAttribute to your project
References
Groovy API
Documentation
Knowledge Base