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.

  1. Create the Contract Header Logic

    1. In Studio, create a new Calculation Logic of Nature Contract Header

      1. name it "ContractHeaderLogic"

      2. label it "Contract Header Logic"

      3. 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.

    2. Add a new Element "PassHeaderDataToLines" with code:

      if (cProcessor.isPrePhase()) { api.global.startDate = cProcessor.contractView.startDate api.global.endDate = cProcessor.contractView.endDate }
    3. Deploy the logic to partition

  2. Set the header logic to be used for the Default Agreement & Promotion Type

    1. in Pricefx Unity, navigate to Agreements & Promotions  Agreement & Promotion Types

    2. set the Header logic to your newly deployed logic

  3. Verify, that you have retainGlobal setting flag setup on your partition:

    1. In Pricefx Unity, navigate to Configuration  System Configuration  General Settings

    2. if you do not have that settings, then cross the checkbox and click Save Changes

    3. 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"

  1. In Studio, create a new Calculation Logic of Nature Contract.

    1. Name it "ContractPromotionDiscount"

    2. for label use "Promotion Discount".

    3. The Validity will be moved backwards, in case you would like to experiment with data from the previous year.

  2. 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]) }
    1. this Element should not display its value in results, unless you want to use that for debugging

  3. 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] }
    1. 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.

  4. 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.

  5. 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
  6. 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() }
  7. 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
    1. ensure this element’s result is displayed on the screen

  8. 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"

    1. the result of this element should be visible in the results

    2. set the Format Type to Money (EUR)

    3. 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.

  9. 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.") }
    1. this value must be in the results

    2. set the Format Type to Money (EUR)

  10. 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.

  11. Create new Condition Type

    1. in Pricefx Unity, navigate to Agreements & Promotions  Condition Types

    2. use Add Condition Type and set up the new type

    3. set the Name to "PromotionDiscount"

    4. set the Label to "Promotion Discount"

    5. set the Pricing Logic to your newly deployed ContractPromotionDiscount logic

    6. 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.

    7. click on Add to add the new type

  12. 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

  13. Test the logic

    1. On logic’s Parameters tab, remember to click Generate Parameters to see all inputs

    2. Setup all the inputs and Test Logic

      1. remember to select the ContractTermTypeName, otherwise you will get unexpected error while trying to Test the Logic.

      2. 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

    3. Test the logic and verify the results. Ensure it works all ok before moving to next steps.

  14. Test the Condition Type in the Unity interface

    1. in Pricefx Unity, navigate to Agreements & Promotions  Agreements & Promotions

    2. Click on New Agreement & Promotion

      1. set the Start Date and End Date to be a range of next quarter (regarding your today)

      2. 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.

      3. we’re ignoring the Customer & Product inputs here for now, because we will deal with them later on the line item

    3. move to Items tab

    4. add the line with Promotion Discount Condition Type

      1. Select your new Contract Term Type PromotionDiscount as new line

      2. Select the Product(s) filter and enter the Promotion Discount size

      3. Click on Recalculate to recalculate the contract and all of its line items

      4. Review the Calculation Results

      5. 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.

  1. In Unity, navigate to menu:Agreements & Promotions [Price Records]

  2. 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.

    1. Rename and Customize the column attribute1

    2. set Name to "ConditionType"

    3. set Label to "ConditionType Type"

    4. set Type to String

  3. 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.

    1. Rename and Customize the column attribute2

    2. set Name to "PromotionDiscount"

    3. set Label to "Promotion Discount"

    4. set Type to Real

    5. set Format Type to Percent

  4. Verify the mapping of the Contract line to the Price Record

    1. in Unity, navigate to Agreements & Promotions  Agreements & Promotions

    2. open the previously saved promotion contract (click on its ID)

    3. Submit the contract for approval

    4. 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.

    5. in Unity, navigate to Agreements & Promotions  Price Records

    6. Verify the new record in this list

      1. you should find there a new record, with SourceID the same, as your Contract ID.

      2. Product Group should match your selection on the contract line item

      3. Valid After and Expiry Date must match to your selection of Start Date and End Date on the Contract Header

      4. Condition Type is mapped from the results of your contract line item

      5. 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.

  1. Modify the Contract Header Logic

    1. In Studio, open your Contract Header Logic

    2. 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 ]) }
    3. Deploy the Contract Header Logic

  2. 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

    1. In Unity, navigate to Configuration  Agreements & Promotions  Agreements & Promotions General Setting

    2. switch the Create Contract Option to the value Calculate new Agreement & Promotion immediately

    3. Click Save to save the setting

  3. Verify the effect of the header logic in user interface

    1. in Unity, navigate to Agreements & Promotions  Agreements & Promotions

    2. click on New Agreement & Promotion

    3. You should notice several input fields are missing now in the emphasized areas

    4. compare this screenshot with the screenshot somewhat above, and review if all the required inputs are hidden

Example of a Solution

promotion-discount.zip

References

Developer Documentation

Accelerators (access based on credentials)

Found an issue in documentation? Write to us.