Create Discounts That Accumulate or Cascade

Apply one or more discounts that accumulate on a single price list.

You can also apply one or more discounts that cascade. For example, on the running net price.

You apply discounts through a simple rule or through a pricing matrix according to different types of adjustments.

  • Discount Amount

  • Discount Percent

  • Markup Amount

  • Markup Percent

  • Price Override

Here are some important concepts.

  • Cascading discount. A calculation that subtracts the discount that each discount rule applies on an item. Here's what cascading discount does.

    • Subtracts discounts in alphabetic, ascending sequence according to rule name.

    • Uses Running Net Price as the basis when it applies each adjustment.

    • Reduces the value of Running Net Price each time it applies the discount for each rule.

    • Doesn't keep the basis constant for each rule. Instead, the discount cascades from rule to rule.

  • Cumulative discount. A calculation that subtracts the discount that each discount rule applies on an item. Here's what cumulative discount does.

    • Subtracts discounts in alphabetic, ascending sequence according to rule name.

    • Accumulates the total discount amount while it applies each rule.

    • Uses List Price as the basis when it applies each adjustment.

    • Doesn't modify the value of List Price when it applies each discount.

    • Keeps the basis constant for each rule. The discount remains the same from rule to rule.

  • Running net price. An object that can go up or down in value while Pricing applies discounts and does other calculations on the price of an item.

You can't create a discount that accumulates or cascades on a configured item.

Summary of the Set Up

  1. Create price element and pricing basis.

  2. Modify pricing algorithms.

  3. Modify pricing algorithms for discounts.

  4. Create discount rules.

  5. Test your set up.

Caution:

The example in this topic describes one way to set up pricing. It is intended only as a general outline that you can use to learn about different ways to set up Pricing. You will need to use different procedures, different steps, different values, and different objects for your implementation, depending on your business requirements.

1. Create Price Element and Pricing Basis

Create the price element and pricing basis that you will use for the running net price.

  1. Go to the Setup and Maintenance work area, then go to the task:

    • Offering: Order Management

    • Functional Area: Pricing

    • Task: Manage Price Elements

  2. On the Manage Price Elements page, click Actions > Add Row, set values, then click Save and Close.

    Attribute

    Value

    Element Code

    ELEMENT_FOR_NET_PRICE

    Element Name

    Running Net Price

    Type

    Price

    Active

    Contains a check mark.

  3. On the Search page, search for, then open Manage Pricing Bases.

  4. On the Manage Pricing Bases page, click Actions > Create, set the values, then click Save and Close > Done > Done.

    Attribute

    Value

    Name

    Running Net Price

    Usage

    Adjustment Basis

    Price Element

    Running Net Price

    Description

    Pricing basis for cumulative and cascading discounts on running net price.

    Active

    Contains a check mark.

Create Service Mapping Attributes

You modify the Sales service mapping differently, depending on whether you use a simple discount or discount according to attribute.

  1. Go to the Pricing Administration work area, then click Tasks > Manage Service Mappings.

  2. On the Manage Service Mappings page, click Sales.

  3. On the Edit Service Mapping page, click Services > Query by Example, enter the value, then press the Enter key on your keyboard.

    Attribute

    Value

    Service

    PriceRequestInternal

Modify Service Mapping for Simple Discount

Here are the modifications that you make in the Sales service mapping.

TermQueue entity of the PriceRequestInternal service

You add the TermName_Custom attribute to the TermQueue entity of the PriceRequestInternal service.

If you need to add your discount on a tier adjustment, then use the TierLineQueue entity instead of the TermQueue entity. If you need to apply a discount according to an attribute, see Add Your Own Attributes to Items in Pricing.

  1. In the Details area, in the Entities tab, click the row that includes the value.

    Attribute

    Value

    Entity

    TermQueue

  2. In the Details area, click Actions > Add Row, then set the values.

    Attribute

    Value

    Entity

    TermQueue

    Read

    Contains a check mark.

    Write

    Contains a check mark.

  3. In the Entities area, click View > Columns > Show All, click Actions > Add Row, set the values, then click Save.

    Attribute

    Value

    Attribute

    TermName_Custom

    Alias

    TermName

    Read

    Contains a check mark.

    Write

    Contains a check mark.

    Type

    String

Modify Service Mapping for Discount According to Attribute

Here are the modifications that you make in the Sales service mapping.

MatrixQueue entity of the PriceRequestInternal service

You add the TermName_Custom attribute to the MatrixQueue entity of the PriceRequestInternal service.

  1. In the Details area, in the Entities tab, click the row that includes the value.

    Attribute

    Value

    Entity

    MatrixQueue

  2. In the Details area, click Actions > Add Row, then set the values.

    Attribute

    Value

    Entity

    MatrixQueue

    Read

    Contains a check mark.

    Write

    Contains a check mark.

  3. In the Entities area, View > Columns > Show All, click Actions > Add Row, set the values, then click Save.

    Attribute

    Value

    Attribute

    TermName_Custom

    Alias

    TermName

    Read

    Contains a check mark.

    Write

    Contains a check mark.

    Type

    String

Modify Service Mapping for Tier Adjustments

You will add the TierQueue entity.

You will add the TierQueue entity.

Try it.

  1. Click Tasks > Manage Service Mappings.
  2. On the Manage Service Mappings page, in the Name column, click Sales.
  3. On the Edit Service Mapping page, click Services.
  4. Click Query By Example, then query for PriceRequestInternal.
  5. In the PriceRequestInternal Details area, on the Entities tab, click Query By Example, then query for TierQueue.
  6. In the TierQueue Entities area, click View > Columns, then make sure Type contains a check mark.
  7. Click Actions > Add Row, then set the values.

    Attribute

    Value

    Name TermName_Custom
    Alias TermName
    Type String
  8. Click Save and Close.

2. Modify Pricing Algorithms

You modify pricing algorithms that calculate the discounts.

Modify the Pricing Algorithm That Applies Discounts

Here's the algorithm that you modify.

pricing algorithm that applies discounts

Modify the pricing algorithm that applies discounts.

  1. Click Tasks > Manage Algorithms.

  2. On the Manage Algorithms page, click Query by Example, enter the value, then press the Enter key on your keyboard.

    Attribute

    Value

    Name

    Apply Discounts

  3. Click Action > Create Version.

  4. In the Name column, click the link for the version you just created.

  5. On the Edit Algorithm page, expand the steps until you locate the Write Pricing Terms step.

  6. Click the row that includes Write Pricing Terms in the Name column.

  7. In the Step Details area, in the First Row Actions area, locate this code:

    //if (TermQuery.Name!=null) ts.Name = TermQuery.Name

  8. Remove the forward slashes ( // ) from the code.

    For example:

    if (TermQuery.Name!=null) ts.Name = TermQuery.Name

    This step instructs the algorithm to examine the TermQuery attribute that you set up in the service mapping earlier in this topic.

  9. Click Save and Close > Actions > Publish.

Modify the Pricing Algorithm That Applies Pricing Terms

Modify the pricing algorithm that applies pricing terms so it can process the running net price.

  1. Click Tasks > Manage Algorithms.

  2. On the Manage Algorithms page, click Query by Example, enter the value, then press the Enter key on your keyboard.

    Attribute

    Value

    Name

    Apply Pricing Terms

  3. Click Actions > Create Version.

  4. In the Name column, click the link for the version that you just created.

  5. Click Variables > Actions > Add Row, set the values, then click Save.

    Attribute

    Value

    Name

    RunningNetPriceElem

    Data Type

    String

    Input/Output

    None

    Default Expression

    'ELEMENT_FOR_NET_PRICE'

    This value identifies the pricing element you created earlier in this topic.

    You must include the single quotation marks (' ).

  6. Click Algorithm.

  7. In the Steps area, click the row that includes Find Applicable Charges in the Name column.

  8. Add the step that creates the running net price.

    • Click Add Step > Composite Step, then click If.

      Here's the step that you add.

      adding step to the algorithm
    • In the Step Details area, set the values, then click Save.

      Attribute

      Value

      Name

      Create the Running Net Price

      Condition

      'DISCOUNT_LINE' == TermType

      You must include the single quotation marks (' ).

    • In the Steps area, click the row that includes Examine Create Running Net Price in the Name column.

    • Click Add Step > Subalgorithm.

      You can add a condition only on the step. So, you create the step, then add the subalgorithm that specifies the pricing algorithm to run and the variable to send to this algorithm.

    • In the Step Details area, set the values.

      Attribute

      Value

      Name

      Create the Running Net Price

      Description

      This step creates a charge component for a running net price.

      Algorithm Name

      Create ChargeComponent with RunningUnitPrice

    • In the Input Variables area, set values.

      Variable

      Assignment Value

      PriceRequest

      PriceRequest

      PriceElementCode

      RunningNetPriceElem

      IsForCeilingPrice

      BaseCeilingPriceElementCode

      Don't use these variables. They are for Oracle internal use only.

      RoundingAdjustmentElementCode

      'QP_ROUNDING_ADJUSTMENT'

      PerformRounding

      false

      IsForMargin

      false

      PriceElementUsageCode

      Leave empty

      ChargeAppliesToCode

      'PRICE'

      RoundingAdjustmentElementUsageCode

      'PRICE_ADJUSTMENT'

    • In the Output Variables area, set the value.

      Variable

      Assignment Value

      PriceRequest

      PriceRequest

  9. Add the step that deletes the running net price.

    step that deletes the running net price

    Add the step.

    • Click the step that has Process Custom Adjustment Flag in the Name column.

    • Click Add Step > Conditional Action, then set the values.

      Attribute

      Value

      Name

      Delete Running Net Price

      Description

      This step deletes a charge component for a running net price.

      Condition

      RunningNetPriceElem == Comp.PriceElementCode

    • In the Data Sets area, click Add Row, then set the value.

      Name

      Variable Path

      Primary

      Comp

      PriceRequest.ChargeComponent

      Contains a check mark.

    • In the Execute Condition area, click Add Condition > Default Action, then set the value.

      Attribute

      Value

      Action

      //This action cascades the discounts. Comp.delete()
    • Click Save.

  10. Make sure the algorithm applies your rules in the sequence that you expect. Do this step when your discount list has more than one rule.

    • Click the step that has Process Other Adjustment Types in the Name column.

    • In the Conditional Actions area, in the Then Perform These Actions column, click the pencil.

    • In the Edit Actions dialog, notice the code.

      finer('\tSetting up queue for applying discount matrix '+TermSetup.AttributePricingMatrixId)
      
      mq = MatrixQ.insert([ParentEntityCode:'CHARGE', ParentEntityId:TermQ.ChargeId])
      mq.DynamicMatrixId = TermSetup.AttributePricingMatrixId
      mq.ApplyToRollupFlag = ('Y' == TermSetup.ApplyToRollupFlag)
      mq.FromCurrencyCode = TermSetup.PricingCurrencyCode
    • Locate the line that has mq.FromCurrencyCode = TermSetup.PricingCurrencyCode. Its the last line of code.

    • Add a new line after the code that you just located, then add mq.TermName=TermSetup.Name to the new line.

      Here's your revised code.

      finer('\tSetting up queue for applying discount matrix '+TermSetup.AttributePricingMatrixId)
      
      mq = MatrixQ.insert([ParentEntityCode:'CHARGE', ParentEntityId:TermQ.ChargeId])
      mq.DynamicMatrixId = TermSetup.AttributePricingMatrixId
      mq.ApplyToRollupFlag = ('Y' == TermSetup.ApplyToRollupFlag)
      mq.FromCurrencyCode = TermSetup.PricingCurrencyCode
      mq.TermName=TermSetup.Name
    • Click OK > Save.

3A. Modify Pricing Algorithms for Simple Discounts

Do this section only if you're applying a simple discount.

  1. Add a function.

    • Click Functions.

      Here's the function that you will add.

      view object that the pricing algorithm can use in a function that gets the adjustment basis

      You add a view object that the pricing algorithm can use in a function that gets the adjustment basis.

    • Click Actions > Add Row, then set the value.

      Name

      Query Type

      getAdjustmentBasis

      View Object Lookup

    • In the Arguments area, add the arguments.

      Name

      Comments

      BasisId

      Value that identifies the adjustment basis.

      Language

      Abbreviation that identifies the language.

    • Click View Object Query > Add Row, then set the values.

      Attribute

      Value

      Application Module

      oracle.apps.scm.pricing.priceExecution.pricingProcesses.publicModel.applicationModule.PricingProcessAM

      Application Configuration

      ${(PriceRequest.PricingServiceParameter[0]?.CacheEnabledFlag==null || PriceRequest.PricingServiceParameter[0].CacheEnabledFlag) ? 'PricingProcessAMShared' : 'PricingProcessAMLocal'}

      View Object

      AdjustmentBasis1

      Single Row

      Contains a check mark.

    • In the Bind Variables area, add the bind variables, then click Save.

      Bind Variable Name

      Bind Variable Value

      basisId

      BasisId

      lang

      Language

  2. Modify the step that gets values for the adjustment basis.

    • Click Algorithm.

    • In the Steps area, click the step that includes Retrieve Basis Values in the Name column.

    • In the Step Details area, in the Default Action area, scroll all the way to the end, and then locate this code:

      else {
        finer('\tFound '+component .PriceElementCode+' charge component '+component .ChargeComponentId+' with unit price '+component .UnitPrice.Value)
        TermQ.AdjustmentBasisValue = component .UnitPrice.Value
       }
    • Add this line immediately before the closing curly bracket:

      //We're adding this line for cascading discounts:
      TermQ.TermName = TermSetup.Name

      For example:

      else {
        finer('\tFound '+component .PriceElementCode+' charge component '+component .ChargeComponentId+' with unit price '+component .UnitPrice.Value)
        TermQ.AdjustmentBasisValue = component .UnitPrice.Value
        //We're adding this line for cascading discounts:
        TermQ.TermName = TermSetup.Name
       }

      You add it here:

      step that gets values for the adjustment basis
    • Click Save.

  3. Modify the step that applies the simple adjustment.

    step that applies the simple adjustment
    • In the Steps area, click the step that includes Apply Simple Adjustments in the Name column.

    • In the Data Sets area, add the data sets.

      Name

      Variable Path

      Cardinality

      RunningPriceComp

      PriceRequest.ChargeComponent

      Many

      This value specifies to use many charge components for the running price to one term in the term queue.

      BasisComp

      PriceRequest.ChargeComponent

      Many

      This value specifies to use many pricing bases for the charge components to one term in the term queue.

    • In the Data Sets area, in the row that includes TermQ in the Name column, set the value.

      Attribute

      Value

      Order By

      TermName

    • In the Execute Condition area, in the Local Variables area, add the local variables.

      Variable Name

      Default

      BasisElementCode

      Leave empty.

      RunningPrice

      Leave empty.

    • In the Default Action area, locate this code.

      AdjustmentValue = AdjustmentValue * (-1)
            }
            }
          else
    • Here's the code that cascades the discount according to the adjustment basis. Add it immediately after the code that you just located.

      // cascade your discount modifications according to the adjustment basis
      {
        BasisElementCode = getBasis(TermSetup.AdjustmentBasisId)?.PriceElementCode
        if (BasisElementCode != null) {
          TermQ.AdjustmentBasisValue = BasisComp.locate([ChargeId: TermQ.ChargeId, PriceElementCode: BasisElementCode])?.UnitPrice?.Value
      }
    • Locate this code.
      finer('    Unit price adjustment = '+AdjustmentValue)
    • Add a closing curly bracket on a new line immediately before the code you just located:
      }
    • Here's the entire modified code. The modification is in bold font:

      def runningUnitPriceCurAdj = Charge.RunningUnitPrice
      def termInDifferentCurrency = (TermSetup.PricingCurrencyCode != null && TermSetup.PricingCurrencyCode != Line.AppliedCurrencyCode && !TermSetup.AdjustmentTypeCode.contains('PERCENT'))
      if (termInDifferentCurrency) {
          convRateRow = locateConversionRate(ConvRate, Header, Line, Charge, PricingTermRule, TermSetup, Message, Line.AppliedCurrencyCode, TermSetup.PricingCurrencyCode)
          if (convRateRow) {
              runningUnitPriceCurAdj *= convRateRow.ConversionRate
          }
      }
      if ('ROOT' == Line.ItemType && 'Y' == TermSetup.ApplyToRollupFlag) {
          finer('    Adjustment type = '+TermSetup.AdjustmentTypeCode)
          finer('    Unit price = '+Charge.RunningUnitPrice)
          finer('    Adjustment amount = '+TermSetup.AdjustmentAmount)
          finer('    Basis value = '+TermQ.AdjustmentBasisValue?:0)    
          AdjustmentValue = pricingUtil.computeUnitAdjustment(TermSetup.AdjustmentTypeCode, runningUnitPriceCurAdj, TermSetup.AdjustmentAmount, TermQ.AdjustmentBasisValue?:0)    
          if (termInDifferentCurrency) {
              convRateRow = locateConversionRate(ConvRate, Header, Line, Charge, PricingTermRule, TermSetup, Message, TermSetup.PricingCurrencyCode, Line.AppliedCurrencyCode)
              if (convRateRow) {
                  AdjustmentValue *= convRateRow.ConversionRate
              }
          }
          if ('ERROR' != PricingTermRule.MessageTypeCode && AdjustmentValue!=null) {
              spread = Spread.insert([ParentChargeId: Charge.ChargeId])
              spread.UnitAdjustment = AdjustmentValue
              //spread.createDataObject('ExtendedAdjustmentAmount')
              //spread.ExtendedAdjustmentAmount.Value = AdjustmentValue
              //spread.ExtendedAdjustmentAmount.CurrencyCode = Charge.CurrencyCode
              spread.PriceElementCode = TermElementCode
              spread.PriceElementUsageCode = priceElementUsageCode
              spread.SourceId = TermSetup.TermId
              spread.SourceTypeCode = 'PRICING_TERM'
              spread.SpreadId = ++spreadId
              // Code Changes for Bug#20265319
              spread.ExplanationMessageName = TermExplanationMsg
          }
      } else {
          finer('    Adjustment type = '+TermSetup.AdjustmentTypeCode)
          finer('    Unit price = '+Charge.RunningUnitPrice)
          finer('    Adjustment amount = '+TermSetup.AdjustmentAmount)
          finer('    Basis value = '+TermQ.AdjustmentBasisValue?:0)
          if ( TermSetup.AdjustmentTypeCode in ['ORA_QP_ACCRUAL_AMOUNT','ORA_QP_ACCRUAL_DIFFERENCE','ORA_QP_ACCRUAL_PERCENT'] )
           { 
            AdjustmentValue = pricingUtil.computeAccrualAdjustment(TermSetup.AdjustmentTypeCode, TermSetup.AccrualTypeCode, runningUnitPriceCurAdj, TermSetup.AdjustmentAmount,TermQ.AdjustmentBasisValue?:0)
            TermElementCode = 'QP_ACCRUAL'
            priceElementUsageCode = null
            //For Un-Reference Return lines, reverse the polarity of adjustment value
            if ( Line.UnreferencedReturnLineFlag ) {
             AdjustmentValue = AdjustmentValue * (-1)
            }
           }
          else {
             BasisElementCode = getBasis(TermSetup.AdjustmentBasisId)?.PriceElementCode
             if (BasisElementCode != null) {
             TermQ.AdjustmentBasisValue = BasisComp.locate([ChargeId: TermQ.ChargeId, PriceElementCode: BasisElementCode])?.UnitPrice?.Value 
             }
             AdjustmentValue = pricingUtil.computeUnitAdjustment(TermSetup.AdjustmentTypeCode, runningUnitPriceCurAdj, TermSetup.AdjustmentAmount, TermQ.AdjustmentBasisValue?:0)
         }
          finer('    Unit price adjustment = '+AdjustmentValue)
          Comp = ChargeComponent.insert([ChargeComponentId:++ServiceParam.ChargeComponentIdCntr])
          Comp.ChargeId = TermQ.ChargeId
          Comp.createDataObject('UnitPrice')
          Comp.UnitPrice.Value = AdjustmentValue
          Comp.UnitPrice.CurrencyCode = Charge.CurrencyCode
          Comp.CurrencyCode = Charge.CurrencyCode
          
          if (termInDifferentCurrency) {
              convRateRow = locateConversionRate(ConvRate, Header, Line, Charge, PricingTermRule, TermSetup, Message, TermSetup.PricingCurrencyCode, Line.AppliedCurrencyCode)
              if (convRateRow) {
                  Comp.UnitPrice.Value *= convRateRow.ConversionRate
              }
          }
      
          if ( Charge.PricedQuantity!=null ) {
              Comp.createDataObject('ExtendedAmount')
              Comp.ExtendedAmount.Value = Comp.UnitPrice.Value*Charge.PricedQuantity.Value
              Comp.ExtendedAmount.CurrencyCode = Comp.UnitPrice.CurrencyCode
              if (Line.ItemType in ['STANDARD', 'COMPONENT', 'ROOT'] && null != Line.ServiceDuration?.Value && null != Line.ServiceDurationPeriodCode ) {
                  Comp.createDataObject('CoverageExtendedAmount')
                  if ('ONE_TIME' == Charge.PriceTypeCode) {
                      Comp.CoverageExtendedAmount.Value =  Comp.ExtendedAmount.Value
                  }
                  else if ('RECURRING' == Charge.PriceTypeCode) {
                      if (Charge.PricePeriodicityCode != Line.ServiceDurationPeriodCode) {
                          // Partial Price Period with Conversion Rate from OKC tables
                          Comp.CoverageExtendedAmount.Value =  Comp.ExtendedAmount?.Value * Charge.PartialPeriodDurationConversionRate?:0                                  
                      }
                      else {
                          //no partial Period pricing based on ServiceDuration      
                          Comp.CoverageExtendedAmount.Value =  Comp.ExtendedAmount.Value * Line.ServiceDuration?.Value     
                      }      
                  }
                  Comp.CoverageExtendedAmount.CurrencyCode = Comp.ExtendedAmount.CurrencyCode
              }
          }
          assert Charge.CompSeqCntr != null
          Comp.SequenceNumber = (Long) Charge.CompSeqCntr++
          Comp.PriceElementCode = TermElementCode
          Comp.PriceElementUsageCode = priceElementUsageCode
          Comp.ExplanationMessageName = TermExplanationMsg
          Comp.PriceValidFrom = Line.PricingDate
          Comp.PriceValidUntil = TermSetup.EndDate
          Comp.SourceId = TermQ.TermId
          Comp.SourceTypeCode = 'PRICING_TERM'
          finest('\tCreated ' + Comp.PriceElementCode + ' charge component ' + Comp.ChargeComponentId + ' with unit adjustment ' + Comp.UnitPrice.Value + ' ' + Comp.UnitPrice.CurrencyCode)
          // Adjust running unit price
           Charge.RunningUnitPrice += Comp.UnitPrice.Value
          if (Line.MessageTypeCode != 'ERROR' && Comp.PriceElementCode == 'QP_ACCRUAL')
             ChannelProgram.ApplicationStatusCode =  'ORA_APPLIED'
          finest('\tAdjusted running unit price by ' + Comp.UnitPrice.Value)
          pricingUtil.computeHeaderCurrencyAmounts(Comp, Charge, Line, Header, ConvRate, Messages)
      }

Make these replacements.

Variable in Code

Attribute from SQL Query You Can Use to Replace the Variable

customer_id

CustomerId

selling_business_unit_id

SellingBusinessUnitId

selling_legal_entity_id

SellingLegalEntityId

inventory_item_id

InventoryItemId

inventory_organization_id

InventoryOrganizationId

unit_code

LineQuantityUOMCode

line_quantity_uom_code

LineQuantityUOMCode

5. Test Your Set Up

  1. Sign into Order Management and create a sales order.

  2. Set the Customer to a value, such as PennyPack Systems.

  3. Click Actions > View Pricing Segment and Strategy, then verify that the Segment is the default segment, and that the Strategy is correct for PennyPack Systems.

  4. In the Order Lines area, search for item AS54888, wait for the results, tab out of the search attribute, then verify that Order Management gets the price from the price list for PennyPack Systems.

  5. Add the AS54888 item to an order line, click Amount on the order line, then verify that Pricing correctly calculates the Net Price and applied the discounts.