Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  1. create a logic

    1. in Studio, create new Calculation Logic, named "QuotePromotion"

      1. the logic has Default Nature

    2. add Element "CustomerId", with Display mode Never and with code:

      Code Block
      return api.customer("customerId")
    3. add Element "ProductId",with Display mode Never and with code:

      Code Block
      return api.product("sku")
    4. add Element "Quantity",with Display mode Never and with code:

      Code Block
      //api.userEntry("Quantity")
      
      final String INPUT_NAME = "Quantity"
      if (api.isInputGenerationExecution()) {
      
          api.inputBuilderFactory().createUserEntry(INPUT_NAME).getInput()
      } else {
          return input[INPUT_NAME]
      }
    5. add Element "AbortOnInputGeneration", with Display mode Never and with code:

      Code Block
      if (api.isInputGenerationExecution()) {
          api.abortCalculation()
      }
    6. add Element "ListPrice", with code:

      Code Block
      // return fictious number for the sake of the training exercise
      
      return 21.3
      1. this value should be displayed

      2. this value is formatted as Money (EUR)

    7. Test the Logic

      1. test with empty parameters, if all ok

      2. test with all parameters entered, if all ok

    8. Deploy the logic to the partition

  2. create a Quote Type

    1. in UnityPricefx, navigate to Quoting  Quote Types

    2. click on Add Quote Type and add new Quote Type

      1. Name: "QuotePromotion"

      2. Pricing Logic: "QuotePromotion"

    3. in Studio, fetch the new Quote Type QuotePromotion to your project, so you can later put it to Git

...

  1. Using the Promotion Discount:

    1. add Element "PromotionDiscountContract", with Display mode _Never, with code:

      Code Block
      final String FIELD_PROMOTION_DISCOUNT = "PromotionDiscount"
      
      def filters = FilterLib.buildFilters("PromotionDiscount", out.ProductId, out.CustomerId)
      
      def highestPromotionDiscount = 0.0
      def contract = [:]
      
      def iter = api.stream("PR", null, ["sourceId", FIELD_PROMOTION_DISCOUNT], *filters)
      iter.each { pr ->                                                   
      
          def discountPct = pr[FIELD_PROMOTION_DISCOUNT] as BigDecimal
      
          if (discountPct > highestPromotionDiscount) {                   
              highestPromotionDiscount = discountPct
      
              contract.highestPromotionDiscount = highestPromotionDiscount
              contract.selectedContractId = pr.sourceId
          }
      
      }
      iter.close()
      
      return contract

      build the Price Record filters only for the specific type of Condition types linesmore Price Records could be available - need to review all of them.In our case, based on the business decision, the higher discount will be used.

    2. add Element "PromotionDiscountPct", with code:

      Code Block
      return out.PromotionDiscountContract?.highestPromotionDiscount
      1. this element’s result should be visible

      2. the value should be formatted as Percentage

    3. add Element "PromotionDiscountContractId", with code:

      Code Block
      def contractId = out.PromotionDiscountContract?.selectedContractId
      if (contractId) {
          return api.getBaseURL() + "/app/#/contracts/detail/${contractId}/contractDetail" 
      }

      Note, that this solution will work only in Unity UI. In Classic UI, you would have to solve the link in a different wayPricefx UI.

      1. this value should be visible

      2. this value must be formatted as Link

    4. Verify, that the logic works as expected

  2. Using the Volume Discount:

    1. add Element "VolumeDiscountContract", with Display mode _Never, with code:

      Code Block
      final String FIELD_VOLUME_DISCOUNT = "VolumeDiscount"
      
      def filters = FilterLib.buildFilters("VolumeDiscount", out.ProductId, out.CustomerId)  
      
      // more PRs could be available - need to solve conflict. In our case, the higher discount wins
      def highestVolumeDiscount = 0.0
      def contract = [:]
      
      def iter = api.stream("PR", null, ["sourceId", FIELD_VOLUME_DISCOUNT], *filters)
      iter.each { pr ->                                                              
      
          def volumeDiscountTiers =
                  api.jsonDecode(pr[FIELD_VOLUME_DISCOUNT])
                     .collect { [(it.key as BigDecimal), (it.value as BigDecimal)] } 
          volumeDiscountTiers.sort { -(it[0]) }
      
          def discountPct = volumeDiscountTiers.find { out.Quantity >= it[0] }?.getAt(1)
      
          if (discountPct > highestVolumeDiscount) {                                 
              highestVolumeDiscount = discountPct
      
              contract.highestVolumeDiscount = highestVolumeDiscount
              contract.selectedContractId = pr.sourceId
          }
      
      }
      iter.close()
      
      return contract

      build the Price Record filters only for the specific type of Condition types linesmore Price Records could be available - need to review all of them.remember, the volume tiers thresholds are stored in a map encoded in JSON format. Review your Price Records created from the Volume Discount contracts to recall the values.In our case, based on the business decision, the higher discount will be used.

    2. add Element "VolumeDiscountPct", with code:

      Code Block
      return out.VolumeDiscountContract?.highestVolumeDiscount
      1. this element’s result should be visible

      2. the value should be formatted as Percentage

    3. add Element "VolumeDiscountContractId", with code:

      Code Block
      def contractId = out.VolumeDiscountContract?.selectedContractId
      if (contractId) {
          return api.getBaseURL() + "/app/#/contracts/detail/${contractId}/contractDetail" 
      }

      Note, that this solution will work only in Unity Pricefx UI. In Classic UI, you would have to solve the link in a different way.

      1. this value should be visible

      2. this value must be formatted as Link

    4. Verify, that the logic works as expected

  3. Invoice Price evaluation - finally, you have to evaluate the Invoice Price, because it is impacted by both types of the contracts.

    1. add Element "InvoicePrice", with code:

      Code Block
      if (out.ListPrice == null) {
          api.addWarning("Cannot calculate Invoice Price, "+
                          "because List Price is not available.")
          return
      }
      
      def promotionDiscount = out.PromotionDiscountPct ?: 0.0
      def volumeDiscount = out.VolumeDiscountPct ?: 0.0
      
      return out.ListPrice * (1.0 - promotionDiscount - volumeDiscount)
      1. the value must be visible

      2. it should be formatted as Money (EUR)

    2. Verify, that the logic works as expected

  4. Deploy your Quote Logic to the partition

...

  1. Let’s expect to have PriceRecords like following:

    QuoteTest PriceRecords
  2. in UnityPricefx, navigate to Quoting  Quotes

  3. Click on New Quote and select the Quote Type QuotePromotion

    QuotePromotion Test
    1. set Effective Date to be within the range of your Price Records

    2. select a Customer, which fits into customer groups used by your Price Records

  4. move to Items tab

    PromotionQuoteItems Test
    1. add a product, which fits the sets of products in your Price Records

    2. enter Quantity so that some VolumeDiscount contract could effect the line

    3. Use Recalculate to ensure, that the Quantity si used for Contract lookup

    4. Verify, if correct values are caclulated

    5. Verify, that the links work ok

...

Documentation (Classic)

...