Agreements & Promotions - Lab 1a
Lab Info
Lesson | Agreements & Promotions |
Category / Topic / Section | Pricefx Core / Agreements & Promotions / CFG 2 |
Target Audience | Certified Configuration Engineer |
Estimated Time to complete | 1:00 h |
Lab Disclaimer: Although this lab offers comprehensive information on the mentioned capability, it lacks a Pricefx environment, that is only available in our instructor-led training, where you can interact with these features and practice using them.
For further insights into the distinctions between our on-demand learning paths and instructor-led training, please click here.
Labs in this section require a specific training partition to complete. To request your training partition please write a request on this email: maros.grman@pricefx.com.
The partition request can take a few days to be processed, once the verification is finished you will receive the credentials by email.
Learning Outcomes
At the end of the Lab, you should be able to:
implement Promotion Contract, so that the end-user can negotiate contract conditions
implement Condition type conditions
implement simple impact analysis, to review impact of the negotiated conditions on the business
Figure 1. Sneakpeek of the implemented Promotion Condition Type
Pre-requisites
In order to be able to complete this laboratory exercise, please complete the prerequisites before you proceed.
Provided resources
Review the provided resources, to get familiar with their content
User Story / Requirements (for Lab 1a & 1b)
As a Sales Manager, I want to negotiate 2 types of promotion conditions with the customer, so that I can give incentive to the customer and increase sales.
As a Sales Manager, I want to see an impact analysis on each line of contract, so that I can easily compare the benefits of the contract with losses.
Details
The 2 types of promotion conditions are:
Promotion Discount
the size of the on-invoice discount is a fixed percentage value
the discount is negotiated for set of products, selected by the manager
Volume Discount
the size of the on-invoice discount depends on the quantity/volume negotiated. So the user need to enter a several pairs of values, for various tiers/levels - e.g.:
when more than 5 pieces are ordered, the discount should be 1%
when more than 10 pieces are ordered, the discount should be 2%
the discount is negotiated for:
set of customers
set of products
The 2 types of on-invoice discounts have a separate adjustments in a waterfall calculation, so they can be later (e.g. on Quote) both used at the same time.
Acceptance Criteria (for Lab 1a)
Main:
A Condition Types exist for "Promotion Discount"
For "Promotion Discount"
User can enter:
Product Group
Promotion Discount size in %
Upon Recalculation, the system provides:
Revenue Last Year - how much money the selected group of customers paid us on invoice price last year. Last year means, that you take the period specified by the user, and shift it back by 1 year. Invoice price data are available in Datamart "Transaction".
Impact on Revenue - how much money would we pay to the group of customers last year (if this discount would be active already last year)
Other:
the product and customer groups are negotiated separately on each promotion line type, i.e. no need for the product and customer group selector on the contract header.
Implementation Steps
If you already have some previous knowledge about implementation/configuration of the Agreements & Promotions capability, we encourage you to try to implement it first on your own based on the information provided in the User Story and Acceptance Criteria.
Step: Implement Header Logic
you will need to be able to use information from the header - startDate and endDate - on the line items (in the Contract Term Types logics). Unfortunately, the information is not passed automatically to the Contract Term Type Logic (it’s not available via api.currentItem()
), so you have to pass it there on your own.
For that you will create a Contract Header logic.
Create the Contract Header Logic
In Studio, create a new Calculation Logic of Nature Contract Header
name it "ContractHeaderLogic"
label it "Contract Header Logic"
set the Valid After date to some point in the past. It’s handy for debugging purposes, so that you can test also on older data, if needed.
Add a new Element "PassHeaderDataToLines" with code:
if (cProcessor.isPrePhase()) { api.global.startDate = cProcessor.contractView.startDate api.global.endDate = cProcessor.contractView.endDate }
Deploy the logic to partition
Set the header logic to be used for the Default Agreement & Promotion Type
in Pricefx Unity, navigate to Agreements & Promotions Agreement & Promotion Types
set the Header logic to your newly deployed logic
Verify, that you have retainGlobal setting flag setup on your partition:
In Pricefx Unity, navigate to Configuration System Configuration General Settings
if you do not have that settings, then cross the checkbox and click Save Changes
in Studio, fetch the retainGlobalAlwaysOn from Advanced configuration, so that you can later store it to Git
In these labs we are not going to be doing any Git operations, which would be required on a real project.
Step: Implement Contract Term Type for "Promotion Discount"
In Studio, create a new Calculation Logic of Nature Contract.
Name it "ContractPromotionDiscount"
for label use "Promotion Discount".
The Validity will be moved backwards, in case you would like to experiment with data from the previous year.
As the user wants to select a set of products, add Element "ProductGroup" with code:
final String INPUT_NAME = "ProductGroup" if (api.isSyntaxCheck()) { api.inputBuilderFactory().createProductGroupEntry().getInput() } else { return ProductGroup.fromMap(input[INPUT_NAME]) }
this Element should not display its value in results, unless you want to use that for debugging
The user wants to be able to set the size of the Promotion Discount as percentage value. This value also needs to be on the Price Record later, so you can make it visible in the results, but in this case not needed, because the input fields values are mapped to Price Record too. Add a new Element "PromotionDiscount":
final String INPUT_NAME = "PromotionDiscount" final String INPUT_LABEL = "Promotion Discount" if (api.isSyntaxCheck()) { api.inputBuilderFactory().createUserEntry(INPUT_NAME) .setLabel(INPUT_LABEL) .setFormatType("PERCENT") .getInput() } else { return input[INPUT_NAME] }
the element result does not need to be displayed in the results. If you would like to do so (maybe for debugging purpose), ensure you set the Format type to Percent.
For calculations, we need to retrieve the information about Start Date, which the user is entering on the contract header. In the Contract Header logic, you’re passing this value to the binding variable
api.global.startDate
. The global variable keeps the values across the whole re-calculation of the contract, so we can read it on the line item. Add a new Element StartDate:if (api.isDebugMode()) { api.global.startDate = api.inputBuilderFactory() .createDateUserEntry("startDate") .getInput() } return api.global.startDate
the input will be used only, when you Test Logic in Studio, because there you will not have the information from the Header available.
In the same way, we need also the endDate. Add new Element EndDate:
if (api.isDebugMode()) { api.global.endDate = api.inputBuilderFactory() .createDateUserEntry("endDate") .getInput() } return api.global.endDate
As you do not need any more input fields, let’s abort the logic, if it is executed in Input Generation mode. Add a new Element AbortOnInputGeneration with code:
if (api.isInputGenerationExecution()) { api.abortCalculation() }
after the contract is approved, you would like to have in the Price Record also the name of the Condition Type. So you will find out the Condition Type of the current line and store it on the line item, so that it is later copied to the Price Record. Make new Element "ConditionType" with code:
return api.currentItem()?.contractTermType
ensure this element’s result is displayed on the screen
The user would like to see the impact of their decisions on the business. Since we cannot see the future, the customer asked to use data from last year as an estimation for the following months. Add a new Element "RevenueLastYear"
the result of this element should be visible in the results
set the Format Type to Money (EUR)
with code:
final String COLUMN_REVENUE = "InvoicePrice" final String COLUMN_DATE = "InvoiceDate" // Find the time period in the previous year for the analysis def startDate = api.parseDate("yyyy-MM-dd", out.StartDate) startDate?.set(year: (startDate ? (startDate[Calendar.YEAR] - 1) : null)) def endDate = api.parseDate("yyyy-MM-dd", out.EndDate) endDate?.set(year: (startDate ? (endDate[Calendar.YEAR] - 1) : null)) def ctx = api.getDatamartContext() def q = ctx.newQuery(ctx.getDatamart("Transaction")) .select("SUM(${COLUMN_REVENUE})", COLUMN_REVENUE) .where( Filter.greaterOrEqual(COLUMN_DATE, startDate), Filter.lessOrEqual(COLUMN_DATE, endDate) ) if (out.ProductGroup) { q.where(out.ProductGroup) } return ctx.executeQuery(q)?.data?.find()?.getAt(COLUMN_REVENUE)
CAUTION: the value of
out.ProductGroup
was selected on top of Product Master table, but the filter is used for the Datamart fields! The mapping is going from ProductMaster to DS Products and it’s fields are brought to the Datamart. You MUST ensure, that all the columns the user can select in the filter, will also be available in the Datamart, otherwise the Query will fail.Datamart queries at the line item level are potential performance risk, especially, when there are a lot of line items in the Contract. In such case it would be good to consider pre-caching already on the header level, if possible.
And finally, the user would like to see the impact of the negotiated Promotion discount on the estimated Revenue (found in the previous element). Add a new element "ImpactOnRevenue" with code:
if (out.RevenueLastYear != null && out.PromotionDiscount != null) { return out.RevenueLastYear * out.PromotionDiscount * -1.0 } else { api.addWarning("Cannot calculate ImpartOnRevenue, because either" + "the Last Year Revenue or Promotion Discount is not available.") }
this value must be in the results
set the Format Type to Money (EUR)
Deploy the Logic ContractPromotionDiscount to the partition
It would be the best to be able to test the logic right now, but before we can test the Logic, we need to have the Condition Type prepared, otherwise you cannot test the Contract Logic in Studio.
Create new Condition Type
in Pricefx Unity, navigate to Agreements & Promotions Condition Types
use Add Condition Type and set up the new type
set the Name to "PromotionDiscount"
set the Label to "Promotion Discount"
set the Pricing Logic to your newly deployed ContractPromotionDiscount logic
the Waterfall Element is optional, and is used to indicate, which adjustment element of the waterfall is influenced by this Contract Term Type. This value is usually not used in the Contract Logic, but later (e.g. on a Quote), when the Contract conditions are applied.
click on Add to add the new type
In Studio, fetch the newly created Contract Term Type PromotionDiscount into your project, so that you can later store it into Git.
created Contract Term Type is the technical name of the Condition Type
Test the logic
On logic’s Parameters tab, remember to click Generate Parameters to see all inputs
Setup all the inputs and Test Logic
remember to select the ContractTermTypeName, otherwise you will get unexpected error while trying to Test the Logic.
with TargetDate you can experiment, you should get different results in the estimations, as you’re moving the data-window you use from the Datamart
Test the logic and verify the results. Ensure it works all ok before moving to next steps.
Test the Condition Type in the Unity interface
in Pricefx Unity, navigate to Agreements & Promotions Agreements & Promotions
Click on New Agreement & Promotion
set the Start Date and End Date to be a range of next quarter (regarding your today)
the Calculation Date will influence, which Price Parameters versions are used data are used for calculations, so it would likely be either day of calculation (usually "today"), or the first day of validity of the Contract.
we’re ignoring the Customer & Product inputs here for now, because we will deal with them later on the line item
move to Items tab
add the line with Promotion Discount Condition Type
Select your new Contract Term Type PromotionDiscount as new line
Select the Product(s) filter and enter the Promotion Discount size
Click on Recalculate to recalculate the contract and all of its line items
Review the Calculation Results
Save your contract, you will need it later
Step: Setup Mapping of the Promotion Contract to Price Record
For either export of contract conditions to external systems, or for easier retrieval of negotiated promotion conditions from another modules, it is useful to export the Contract Term Type lines into Price Records.
The out-of-the-box fields are mapped automatically, but for your new fields, you have to setup the mapping. The mapping is done only by matching of names - the name of the result(or input) on the line item must match the name of the column on Price Record.
Both input fields and output fields can be mapped to the Price Record.
In Unity, navigate to menu:Agreements & Promotions [Price Records]
Setup a column to keep the name of the Condition Type, so that we know, what type of conditions are stored on that line. The value of the column will come from the result ConditionType of the line item.
Rename and Customize the column attribute1
set Name to "ConditionType"
set Label to "ConditionType Type"
set Type to String
Setup a column to store the Promotion Discount percentage value. The value of the column will come from the input field PromotionDiscount of the line item.
Rename and Customize the column attribute2
set Name to "PromotionDiscount"
set Label to "Promotion Discount"
set Type to Real
set Format Type to Percent
Verify the mapping of the Contract line to the Price Record
in Unity, navigate to Agreements & Promotions Agreements & Promotions
open the previously saved promotion contract (click on its ID)
Submit the contract for approval
since there’s no approval workflow now, it does not require any approval, and so gets through immediately, and the mapping of the contract line items to the Price Records happens also immediately.
in Unity, navigate to Agreements & Promotions Price Records
Verify the new record in this list
you should find there a new record, with SourceID the same, as your Contract ID.
Product Group should match your selection on the contract line item
Valid After and Expiry Date must match to your selection of Start Date and End Date on the Contract Header
Condition Type is mapped from the results of your contract line item
Promotion Discount is mapped from the input of your contract line item
Step: Hide out-of-the-box inputs on the Header
The user does not need to see the Product and Customer Group selector, which is added by default to the Header. Additionally, some other inputs are not utilized on this contract type, so the user does not see them.
Modify the Contract Header Logic
In Studio, open your Contract Header Logic
Add a new Element named "HideInputFields", with code:
if (cProcessor.isPrePhase()) { cProcessor.setRenderInfo("userGroupEdit", "hide", true) cProcessor.setRenderInfo('contractExternalRef', 'hide', true) cProcessor.addOrUpdateInput([ "name": "ProductGroup", "type": InputType.HIDDEN ]) cProcessor.addOrUpdateInput([ "name": "CustomerGroup", "type": InputType.HIDDEN ]) }
Deploy the Contract Header Logic
As we do want to hide the header fields immediately when user creates a new contract, we need to set it up in the system
In Unity, navigate to Configuration Agreements & Promotions Agreements & Promotions General Setting
switch the Create Contract Option to the value Calculate new Agreement & Promotion immediately
Click Save to save the setting
Verify the effect of the header logic in user interface
in Unity, navigate to Agreements & Promotions Agreements & Promotions
click on New Agreement & Promotion
You should notice several input fields are missing now in the emphasized areas
compare this screenshot with the screenshot somewhat above, and review if all the required inputs are hidden
Example of a Solution
References
Developer Documentation
Accelerators (access based on credentials)
Found an issue in documentation? Write to us.