...
create a logic
in Studio, create new Calculation Logic, named "QuotePromotion"
the logic has Default Nature
add Element "CustomerId", with Display mode Never and with code:
Code Block return api.customer("customerId")
add Element "ProductId",with Display mode Never and with code:
Code Block return api.product("sku")
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] }
add Element "AbortOnInputGeneration", with Display mode Never and with code:
Code Block if (api.isInputGenerationExecution()) { api.abortCalculation() }
add Element "ListPrice", with code:
Code Block // return fictious number for the sake of the training exercise return 21.3
this value should be displayed
this value is formatted as Money (EUR)
Test the Logic
test with empty parameters, if all ok
test with all parameters entered, if all ok
Deploy the logic to the partition
create a Quote Type
in UnityPricefx, navigate to Quoting Quote Types
click on Add Quote Type and add new Quote Type
Name: "QuotePromotion"
Pricing Logic: "QuotePromotion"
in Studio, fetch the new Quote Type QuotePromotion to your project, so you can later put it to Git
...
Using the Promotion Discount:
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.
add Element "PromotionDiscountPct", with code:
Code Block return out.PromotionDiscountContract?.highestPromotionDiscount
this element’s result should be visible
the value should be formatted as Percentage
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.
this value should be visible
this value must be formatted as Link
Verify, that the logic works as expected
Using the Volume Discount:
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.
add Element "VolumeDiscountPct", with code:
Code Block return out.VolumeDiscountContract?.highestVolumeDiscount
this element’s result should be visible
the value should be formatted as Percentage
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.
this value should be visible
this value must be formatted as Link
Verify, that the logic works as expected
Invoice Price evaluation - finally, you have to evaluate the Invoice Price, because it is impacted by both types of the contracts.
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)
the value must be visible
it should be formatted as Money (EUR)
Verify, that the logic works as expected
Deploy your Quote Logic to the partition
...
Let’s expect to have PriceRecords like following:
in UnityPricefx, navigate to Quoting Quotes
Click on New Quote and select the Quote Type QuotePromotion
set Effective Date to be within the range of your Price Records
select a Customer, which fits into customer groups used by your Price Records
move to Items tab
add a product, which fits the sets of products in your Price Records
enter Quantity so that some VolumeDiscount contract could effect the line
Use Recalculate to ensure, that the Quantity si used for Contract lookup
Verify, if correct values are caclulated
Verify, that the links work ok
...
Documentation (Classic)
...