Lab Info
Lesson: Product Discount Data Management (Standalone Custom Form)
Target Audience: Certified Configuration Engineer
Estimated Time to complete: TBD
Requirements & Solution Author: Petr Rys
Solution Developer & Lab Author: Marcin Ćuczakuczak
User Story / Requirements
Create a standalone Custom Form to help pricing managers manage Discounts, Product Groups and Families from one place.
Product Group is part of Product Master, Product Family is assigned per Product Group.
Each Product group belongs to 1 Product Family. (Product Family Mapping table)
Based on cumulative yearly revenue on the whole Product Family the customer has assigned Discount Level in Discount Level Definition Table.
Based on Product Group and Discount Level achieved by the Customer the sales agent has a "recommended discount" and a "maximum allowed discount" (Target Discount % and Max Discount %)
Details
Product Discount Data Management Custom Form allows users to perform these tasks:
Change Data → select a table and change values within the table without adding new rows.
Add new Product Group → add a new group, assign it to a product family, and assign all the required discounts for different levels. It is important that the discount for each additional level is greater than or equal to the discount for the previous level; otherwise, the validation process will fail.
Add a product family → reassign product groups to reflect a new product family.
Add new discount level to existing product family → create new discount levels for an existing product family. The levels must be continuous, starting from level 1 at 0 Min Revenue. Each additional level must have a minimum revenue greater than or equal to the previous level. Users can assign new discounts for these new levels, ensuring that the discount for each additional level is greater than or equal to the discount for the previous level; otherwise, the validation will fail.
Learning Outcomes
At the end of the Lab, you should be able to:
Create Standalone Custom Form, Custom Form Type
Utilize Input Matrix as Custom Form input
Understand Custom Forms Workflow
Provided resources
Logics:
(sCFO_ProductDiscountLib)
Company Parameters:
(ProductFamilyMapping, DiscountLevelDefinition, Discount)
Acceptance Criteria
User can access a standalone Custom Form within the Analytics section to complete tasks listed in the Details section.
User can select one of three options: Modify data, Create new Product Group, or Create new Product Family.
A separate form will be displayed for each option
Tables with calculation results are available in the Calculations section, located on the right panel.
It is recommended to highlight any changes made to tables for better visibility and clarity.
After the user submits the form, changes should be saved in the corresponding company parameter tables. If a workflow has been added to the form, the changes should be saved after approval.
Sample solution
Step-by-step solution
Develop the logic and test in Studio
Go to the Pricefx Studio and create the following logics.
a) sCFO_ProductDiscountDataMgmt
Pricefx -> Create Calculation Logic
Nature: Custom form header (customFormHeader)
Name: sCFO_ProductDiscountDataMgmt
Add two new elements to your logic:
Configurator
Calculation
b) sCFO_Workflow
Pricefx -> Create Workflow Logic
Nature: DEFAULT
Workflow Type: Custom Form (customform)
Name: customform
Add two new elements to your logic with the suggested code:
AbortOnInputGenerationExecution
if (api.isInputGenerationExecution()) { api.abortCalculation() }
BuildStep
workflow.addWatcherStep("Sales Manager") .withUserWatchers("admin") .withReasons("Sales manager needs to approve this form") workflow.addApprovalStep("Sales Manager") .withPostStepLogic("customform_WorkflowPostStep") .withApprovers("admin") .withReasons("Sales manager needs to approve this form")
This is a sample solution that you can customize according to your requirements. This logic contain two steps. Pay attention to line number 6, which is the name of the Post Step logic that you will create in the next step.
c) customform_WorkflowPostStep (This logic is executed after all steps in the sCFO_workflow logic have been approved.)
Pricefx -> Create Workflow Logic
Nature: Post step (wfStepLogic)
Name: customform_WorkflowPostStep
Add a new element to your logic with the suggested code:
UpdateData
if (workflowHistory.steps.findAll { it.approvalStep }.every { it.approved } || workflowHistory.activeStep?.isEmpty()) { //TODO: The logic that will be performed after the user approved all the steps. }
Pay attention to the condition. The first part checks if all steps have been approved. The second part checks if we have added any steps to our Workflow logic.
Proceed with deploying all created logics to your partition.
Access Administration → Custom Form Types from your partition. Create a new form type by clicking on the [+ New Form Type] button located at the top-right corner of the screen.â Uncheck Embedded option and fill in the form:
If you miss [+ New Form Type] button, add Custom Forms rights to your user (admin) using User Admin.
Add the following code in the Configuration section.
{ "name": "Default CFO config", "steps": [ { "icon": "bars", "name": "step1", "tabs": [ { "icon": "dashboard", "name": "details", "type": "details", "translationKey": "Management options" }, { "icon": "process", "name": "workflow", "type": "workflow", "translationKey": "Workflow" }, { "icon": "file-check-alt", "name": "actions", "type": "actions", "translationKey": "Todo list" } ], "translationKey": "Product Discount Data Management" } ] }
Here's a breakdown of what each component represents:
"name": "Default CFO config" - this is the name of the configuration, which can be used for identification and reference purposes.
"steps": [ ... ] - this is an array of steps, where each step represents a tab in the CFO feature. In this example, there is only one step (with the name "step1").
{ ... } - this is the object representing the current step. In this example, it has three tabs (with the names "details", "workflow", and "actions").
"name": "details" - this is the name of the tab, which will be displayed in the user interface.
"icon": "dashboard" - this is the icon for the tab. In this example, the icon for the "details" tab is represented by the "dashboard" font awesome icon.
"name": "workflow" - this is the name of the second tab.
"name": "actions" - this is the name of the third tab.
"type": "details"/"workflow"/"actions" - this is the type of the tab. In this example, there are three different types of tabs ("details", "workflow", and "actions").
"translationKey": "Management options"/"Workflow"/"Todo list" - this is the key used for translation of the tab labels. These keys should be defined in a property file.
"translationKey": "Product Discount Data Management" - this is the key used for translation of the step label.
In this configuration, the first tab is named "details" and uses the "dashboard" icon. It has a name that can be translated using the "Management options" key. The second tab is named "workflow" and uses the "process" icon. It has a name that can be translated using the "Workflow" key. The third tab is named "actions" and uses the "file-check-alt" icon. It has a name that can be translated using the "Todo list" key. The step label itself is also translatable using the "Product Discount Data Management" key.
Pricefx Studio - Create Custom Form Type
Using the Create Custom Form Type feature in Pricefx Studio simplifies the process of creating a Custom Form Type as compared to manual creation through the user interface. To create a custom form type in Pricefx, please follow these steps:
Right-click on the project name and select "Pricefx", then choose "Create Custom Form Type".
Provide a name for the new custom form type.
Fill in the fields provided below in the General tab.
Property | Value |
---|---|
Name | ProductDiscountManager |
Label | Product Discount Manager |
Header Logic Name | sCFO_ProductDiscountDataMgmt |
Workflow Logic Name | customform |
Mode | Module |
Module | ANALYTICS |
Navigate to the Steps tab and fill out all the necessary information. Add three Step Tabs, and set Name, Type, Icon, and Translation Key for each.
Name | Type | Icon | Translation Key |
---|---|---|---|
dashboard | details | dashboard | Management otions |
workflow | workflow | process | Workflow |
actions | actions | file-check-alt | Todo list |
Replicate the process for the "Tabs" tab.
After you have provided all the necessary information, you can deploy the changes to your partition.
Access Administration → Custom Form Types from your partition. A new Custom Form Type should be visible on the list and in the Analytics menu.
In the image below, you will see two options for Product Discount Manager. The first one was manually created, and the second one was created in Pricefx Studio.
Verify if you have completed all the tasks correctly.
Go to Administration → Logics. Navigate to the left-hand panel and select "General Workflow Logic". You should see your customform logic on the list.
Go to Administration → Logics. Navigate to the left-hand panel and select "Workflow Post Step Logics". You should see your customform_WorkflowPostStep logic on the list.
Go to Administration → Logics. Navigate to the left-hand panel and select "Custom Forms". You should see your sCFO_ProductDiscountDataMgmt logic on the list.
Our newly created option should now appear on the left-hand side menu.
Go to the specified location and create a new form. Customize the label as per your choice, then click on the [Add] button to confirm your selection. If you don't assign a label to your form, the system will automatically set a default label.
You should see a view similar to this:
Compare the form with the code in Configuration section.
Fetch sCFO_ProductDiscountLib to your partition.
This logic contains two elements:
ConstConfig
Includes permanent parameters such as formatting elements, options description, table names, and other configuration elements.
CfoProductConfiguratorUtils
Includes some methods that may be helpful when creating a form.
Navigate to the sCFO_ProductDiscountDataMgmn logic and open the Configurator.groovy file. Once you have accessed the file, add the following code snippet to it.
if (customFormProcessor.isPrePhase()) { //1. Create two variables named 'configuratorName' and 'cfoFormIdKey'. Assign values to them from the Constconfig file. //String configuratorName = //String cfoFormIdKey = //2. Assign value to currentCfoForm. Use api.currentItem() //def currentCfoForm = //3. Get id from current Item. Remember that current item can be null. //Long cfoFormId = //4. Get inputs from currentCfoFrom. //5. Find input with a name that matches configuratorName //6. Take value //7. If the above operation returns null, return an empty map [:]. You can use elvis operator '?:' //If you want to prevent NullPointerException, it is recommended to use the safe navigation operator '?'. This will ensure that your code doesn't throw an exception if a null value is encountered. //Map configuratorValueBackup = //8. Add current form id to the configuratorValueBackup. Use cfoFormIdKey as a key and cfoFormId as value. //configuratorValueBackup.put() //9. update the input "ROOT" with a map that provides the name, label, url, type and value. customFormProcessor.addOrUpdateInput("ROOT", [name : configuratorName, label: configuratorName, url : configuratorName, type : InputType.INLINECONFIGURATOR, value: configuratorValueBackup] ) }
Take a look at line 4 and the value of configuratorName, which will be created in the next step.
DEBUGGING
Make sure to set the proper CustomForm for your logic in the Inputs tab. Run and test the logic afterwards.
If you want to verify the results at each step, you can use the 'api.trace()' function.
Make sure that you receive values that are similar to these.
If everything appears to be in order, you may proceed with the next step.
Create sCFO_ProductDiscountHeader_Configurator logic.
Pricefx -> Create Calculation Logic
Nature: DEFAULT
Name: sCFO_ProductDiscountHeader_Configurator
Add new elements to your logic:
ConfiguratorEntry
OptionInput
ChangeDataInputs
a) add the following code snippet to ConfiguratorEntry.groovy.
In this step, we will create the configuratorEntry object and add a new hidden input with our custom form id to the configuratorEntry before returning it.
import net.pricefx.server.dto.calculation.ConfiguratorEntry // Create a key for the CFO form ID from specified library //String cfoFormIdKey = // Create a new ConfiguratorEntry using the API //ConfiguratorEntry configuratorEntry = // Create a HiddenEntry InputBuilder with the CFO form ID key and build a context parameter for it //def param = // Add the newly created parameter to the ConfiguratorEntry. Use createParameter method. // Return the modified ConfiguratorEntry
a) add the following code snippet to OptionInput.groovy.
In this step, we will add a header and a drop-down list with options for selecting individual forms to the configurator.
// Initialize a constant for main paragraph message //String PARAGRAPH_MESSAGE_MAIN = // Get value of the first input from the ConfiguratorEntry, only if it exists Long formId = // Get the product discount configurator utility script from the product discount library Script productDiscountConfigurator = // Instantiate the main entry of the product discount configurator. // Use getMainConfiguratorEntry method with formId as a parameter ConfiguratorEntry mainConfiguratorEntry = // Set the paragraph header for the main configurator entry. Use the constant initialized earlier // Return the main configurator entry
a) add the following code snippet to ChangeDataInputs.groovy.
In this step, we retrieve the option selected by the user and pass it as a parameter to the method that changes the form depending on the selected option.
import net.pricefx.server.dto.calculation.ConfiguratorEntry // Get value of the first input from the OtionInput.groovy, only if it exists String selectedType = // Use configuratorSwitch method. Pass selectedType as a parameter. /** * This method is used to select and configure different types of ConfiguratorEntries. * It also loads the correct messages for each type. * * @param selectedType The type of ConfiguratorEntry to be used. This could be 'CHANGE_DATA', 'NEW_FAMILY', or 'NEW_GROUP'. * @return A ConfiguratorEntry instance configured based on the selectedType. * If the getterMethod or message for the selectedType do not exist, the method will return null. */ ConfiguratorEntry configuratorSwitch(String selectedType) { Script CONFIG_UTILS = libs.sCFO_ProductDiscountLib.CfoConfiguratorUtils Script CONST_CONFIG = libs.sCFO_ProductDiscountLib.ConstConfig def getterMap = [ (CONST_CONFIG.CHANGE_DATA) : CONFIG_UTILS.&getChangeDataConfiguratorEntry, (CONST_CONFIG.NEW_FAMILY) : CONFIG_UTILS.&getAddFamilyConfiguratorEntry, (CONST_CONFIG.NEW_GROUP) : CONFIG_UTILS.&getAddGroupConfiguratorEntry, ] def messageMap = [ (CONST_CONFIG.CHANGE_DATA) : CONST_CONFIG.CFO_CONFIGURATOR_CONFIG.PARAGRAPH_MESSAGE_CHANGE, (CONST_CONFIG.NEW_FAMILY) : CONST_CONFIG.CFO_CONFIGURATOR_CONFIG.PARAGRAPH_MESSAGE_ADD_FAMILY, (CONST_CONFIG.NEW_GROUP) : CONST_CONFIG.CFO_CONFIGURATOR_CONFIG.PARAGRAPH_MESSAGE_ADD_GROUP, ] def getterMethod = getterMap[selectedType] def message = messageMap[selectedType] if (getterMethod && message) { return configureEntries(getterMethod, message) } } /** * This method applies configuration to entries. * * @param getterMethod Method reference for getting configurator entry. * @param message The message to be set as paragraph header in configurator entry. * @return ConfiguratorEntry The modified configurator entry with new paragraph header. */ ConfiguratorEntry configureEntries(def getterMethod, String message) { ConfiguratorEntry configuratorEntry = getterMethod.call() configuratorEntry.setMessage(message) return configuratorEntry }
EXPECTED RESULT
DEBUGGING
Open logic.json in sCFO_ProductDiscountHeader_Configurator.
Open Studio Editor.
Click on the Inputs tab.
Set the Context to CONFIGURATOR.
Generate Inputs.
Click on the Results tab and check output.
Your output should resemble the example given below.
The next step is to design forms for each of the three available options. Here are the expected results presented below.
Change data option. Three separate forms will be created based on the selected choice in the "Select table" dropdown.
a) Product Family Mapping
Requirements
"Select Product Group or Product Family" is a drop-down list with two options: Product Group, Product Family
“Select the value you want to change” is drop-down list with unique values from ProductGroup/Product Family colums
“New name” is input type of String
All fields are required.
b) Discount Level Definition
Requirements
"Select table" is a drop-down list with three options: Product Family Mapping, Discount Level Definition, and Discount.
After selecting the table we want to modify, a table should display the current data from the corresponding Company Parameters table. The table is not editable.
The user is permitted to modify only a single row of the table.
Use the drop-down list to set keys for each table.
All non-key fields must be updated with edited fields that correspond to their respective values.
For Discount Level Definition option:
For Product Family Mapping option:
For Discount option:
All fields are required.
Add new Product Group option
Requirements
“New Product Goup“ is an input type of String
“Assign Product Family” is a drop-down list with unique values of Product Familty colum in Discount Level Definition company parameter table.
The table for adding and deleting discount records should have identical columns as the Discount company parameters table, and provide a user-friendly way to add records.
“Product Group” column shoud have a drop-down list with newly added “New Product Group”. ColumnType: Option
“Discount Level” coulumn should have a drop-down list with list of numbers from 1 to 6. columnType: Option
“Traget Discount” and “Max Discount” are coulmns with columnType: Numeric
All fields are required.
Add new Product Family option
Requirements
“New Product Family“ is an input type of String
“Assign new Product Family to an already existing Product Group.” is a checkbox. Default: unchecked.
“Assign to:” is a drop-down list. This is a list of unique values from “Product Family” column in Discount Level Definition company parameters table. This filed will be visible if the “Assign new Product Family to an already existing Product Group” value is set to true (with the checkbox checked).
The table for adding and deleting new discount levels records should have identical columns as the Discount Level Definition company parameters table, and provide a user-friendly way to add records.
“Product Family” column shoud have a drop-down list with newly added “New Product Family”. ColumnType: Option
“Discount Level” and “Min Revenue” are coulmns with columnType: Numeric
All fields are required.
Solution
Go to ChangeDataInputs.groovy and look at the configuratorSwitch method. Implement methods for each option chosen by the user to create one of three forms. Each method returns the ConfiguratorEntry object.
/* Fragment of configuratorSwitch method */ def getterMap = [ (CONST_CONFIG.CHANGE_DATA) : CONFIG_UTILS.&getChangeDataConfiguratorEntry, (CONST_CONFIG.NEW_FAMILY) : CONFIG_UTILS.&getAddFamilyConfiguratorEntry, (CONST_CONFIG.NEW_GROUP) : CONFIG_UTILS.&getAddGroupConfiguratorEntry, ]
getChangeDataConfiguratorEntry()
ConfiguratorEntry getChangeDataConfiguratorEntry() { ConfiguratorEntry changeDataSection = api.createConfiguratorEntry() //TODO: create changeDataSection //1. Get config map from libraray (CFO_CONFIGURATOR_CONFIG) //2. Create option input - name: "ppTableInput", label: "Select table", required: true. // Get the value from CFO_CONFIGURATOR_CONFIG by OPTIONS.TABLE_OPTIONS key and // pass it as options parameter. //3. Get value from ppTableInput //4. Display a specific group of fields based on the option that is chosen. // Check required inputs in follow sections: // - For Discount Level Definition option // - For Product Family Mapping option // - For Discount option return changeDataSection }
For Discount Level Definition option:
label | name | type | |
---|---|---|---|
1 | Discount Level Definition |
|
|
2 | Select Product Family you want to change. |
|
|
3 | Select Discount Level |
|
|
4 | New Min Revenue |
|
|
Discount Level Definition: data from
DiscountLevelDefinition
lookup table. Read-only table.Select Product Family you want to change.: list distinct values found in the "Product Family" column from the
DiscountLevelDefinition
table.Select Discount Level: Discount levels for selected Product Family.
For Product Family Mapping option:
label | name | type | |
---|---|---|---|
1 | Product Family Mapping |
|
|
2 | Select Product Group or Product Family |
|
|
3 | Select the value you want to change. |
|
|
4 | New name |
|
|
Product Family Mapping: data from
ProductFamilyMapping
lookup table. Read-only table.Select Product Group or Product Family: two options [“Product Group“, “Product Family”]
productGroupOption
orproductFamilyOption
: The items are shown according to the selection made in thechangeGroupOrFamily
optionproductGroupOption
: list of distinct values found in the "Product Group" column from theProductFamilyMapping
table.productFamilyOption
: list of distinct values found in the "Product Family" column from theProductFamilyMapping
table.
For Discount option:
label | name | type | |
---|---|---|---|
1 | Discount |
|
|
2 | Select the value you want to change. |
|
|
3 | Select Discount Level |
|
|
4 | New Target Discount % |
|
|
5 | New Max Discount % |
|
|
Discount: data from
Discount
lookup table. Read-only table.Select the value you want to change.: list distinct values found in the "Product Group" column from the
Discount
table.Select Discount Level: Discount levels for selected Product Group.
getAddFamilyConfiguratorEntry()
ConfiguratorEntry getAddFamilyConfiguratorEntry() { ConfiguratorEntry productFamilySection = api.createConfiguratorEntry() //TODO: create productFamilySection //1. Create two inputs: // a) InputType.STRINGUSERENTRY, name: "productFamilyInput", label: "New Product Family", required: true // b) InputType.BOOLEAN, name: "assignToGroup", label: "Assign new Product Family to an already existing Product Group." //2. Get input value from "assignToGroup". If value is true: // a) Find unique family names from the ProductFamilyMapping company parameters table. // b) Create option input - name: "assignProductGroup", label: "Assign to", pass values from step a) //3. Create a table. Use result from step 1a as a option for product family option. return productFamilySection }
label | name | type | |
---|---|---|---|
1 | New Product Family |
|
|
2 | Assign new Product Family to an already existing Product Group. |
|
|
3 | Assign to |
|
|
4 | New Discount Levels |
|
|
Assign to: list of unique names from
ProductFamilyMapping
lookup tableNew Discount Levels: user can add and delete rows.
getAddGroupConfiguratorEntry()
ConfiguratorEntry getAddGroupConfiguratorEntry() { ConfiguratorEntry productGroupSection = api.createConfiguratorEntry() //TODO: create productGroupSection //1. Find unique family names from the ProductFamilyMapping company parameters table. // You can use the API function "findLookupTableValues". //2. Create two inputs: // a) InputType.STRINGUSERENTRY, name: "productGroupInput", label: "New Product Group", required: true // b) InputType.Option, name: "Assign Product Family", label: "assignProductFamily", required: true // Pass the values from first step as a setValuesOptions() parameter //3. Get input value from productGroupInput //4. Create a table. Use result from step 3 as a option for product group option. return productGroupSection }
label | name | type | |
---|---|---|---|
1 | New Product Group |
|
|
2 | Assign Product Family |
|
|
3 | New Discount |
|
|
Assign Product Family: list of unique values from
ProductFamilyMapping
lookup tableNew Discount: user can add and delete rows
Calculations
The results will be displayed as tables in the right panel. Only tables with changes will be shown as copies of parameter tables with updated values.
Option | Tables that should be displayed | |
---|---|---|
1 | Change data → Product Family Mapping → Product Group | Discount Table Product Family Mapping |
2 | Change data → Product Family Mapping → Product Family | Product Family Mapping Discount Level Definition |
3 | Change data → Discount Level Definition | Discount Level Definition |
4 | Change data → Discount | Discount Table |
5 | Add new Product Group | Product Family Mapping Discount Level Definition |
6 | Add new Product Family | Product Family Mapping Discount Level Definition |
Open the Calculation.groovy file, which is located in the sCFO_ProductDiscountDataMgmt logic. Once it's open, please add the following code to the file.
if (customFormProcessor.isPostPhase()) { // choosenOption is a value from managementOptionsInput if (chosenOption == "Add new Product Group") { //TODO: Part-1 } else if (chosenOption == "Add new Product Family") { //TODO: Part-2 } else if (chosenOption == "Change data") { //TODO: Part-3 if (ppTableInput == "Product Family Mapping") { //TODO: Part-4 if (chosenTable == "Product Family") { //TODO: Part-5 } else if (chosenTable == "Product Group") { //TODO: Part-6 } //TODO: Part-7 } else if (ppTableInput == "Discount Level Definition") { //TODO: Part-8 } else if (ppTableInput == "Discount") { //TODO: Part-9 } } }
Steps to follow | ||
---|---|---|
1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
Code samples
Task | Sample Code |
---|---|
Create a table | ResultMatrix newTable = api.newMatrix('Column_1', 'Column_2',) newTable.setColumnFormat('Column_1', FieldFormatType.TEXT) newTable.setColumnFormat('Column_2', FieldFormatType.NUMERIC) |
Get data from company parameter table | def getDiscount() { def id = api.findLookupTable("Discount").id def filters = [ Filter.equal('lookupTable.id', id) ] def discountTable = api.find('MLTV2', 0, 0, null, *filters) def rows = discountTable.collect { [ productGroup : it.key1, discountLevel : it.key2, targetDiscount: it.attribute1, maxDiscount : it.attribute2, typedId : it.typedId] } return rows } |
How to edit cell style in table | def cell_1 = newTable.styledCell(example_value, "black", "yellow", "bold") def cell_2 = newTable.styledCell(example_value, "black", "yellow", "bold") newTable.addRow(cell_1, cell_2) |
How to add new table to the customFormProcessor. | customFormProcessor.addOrUpdateOutput( [ "resultName" : "new_name", "resultLabel": "new_label", "resultType" : "MATRIX", "result" : newTable, ] ) |
Saving changes to the tables
We need to ensure that changes in the target tables are displayed accurately. Once the user approves the changes, they should be saved. The customform_WorkflowPosStep logic is responsible for this process.
We need to revisit the logic that controls our Workflow. Our logic consists of two steps. Take a look at BuildStep.groovy
workflow.addWatcherStep("Sales Manager") .withUserWatchers("admin") .withReasons("Sales manager needs to approve this form") workflow.addApprovalStep("Sales Manager") .withPostStepLogic("customform_WorkflowPostStep") .withApprovers("admin") .withReasons("Sales manager needs to approve this form")
In line number 6, we see a reference to logic, which will be performed after the user approval. Open the UpdateData.groovy file in customform_WorkflowPosStep logic and add the code to consolidate data in the Company Parameters tables.
This task can be approached in various ways. You can either attempt to preserve the data or use the provided solution below.
if (workflowHistory.steps.findAll { it.approvalStep }.every { it.approved } || workflowHistory.activeStep?.isEmpty()) { //part-1 if (optionInput == "Change data") { if (changeGroupOrFamily == "Product Group") { //part-2 } else if (changeGroupOrFamily == "Product Family") { //part-3 } else if (ppTableInput == "Discount") { //part-4 } else if (ppTableInput == "Discount Level Definition") { //part-5 } } else { //part-6 if (optionInput == "Add new Product Family") { //part-7 } else if (optionInput == "Add new Product Group") { //part-8 } } }
Steps to follow | |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Code samples
Task | Code samples |
---|---|
Get user inputs from current item assign it to | api.currentItem()?.inputs?.get(0)?.value |
Get individual field values from user | inputs?.get("managementOptionsInput") |
Get | def discountID = api.findLookupTable("Discount").id |
Create filters:
| def discountFilters = [ Filter.equal("ProductGroup", oldName), Filter.equal('lookupTable.id', discountID) ] |
Update | api.find('LTV', 0, *discountFilters).collect { def entry = [ lookupTableName: "ProductFamilyMapping", id : it.id, valueType : it.valueType, typedId : it.typedId, name : newName, value : it.value ] api.addOrUpdate("LTV", entry) } |
Use | discountEntries.each { api.addOrUpdate("MLTV2", [ lookupTableName: "Discount", key1 : it["Product Group"].value, key2 : it["Discount Level"].value, attribute1 : it["Target Discount %"].value, attribute2 : it["Max Discount %"].value ]) } |
Get | api.currentItem()?.outputs?.find { it.resultName == "DiscountLevelDefinitionMatrix" } |
If output is null get an empty list | discountLevelDefinitionTable = discountLevelDefinitionTable ? discountLevelDefinitionTable : [] |
Filter the rows in their respective tables based on the values of Map. | def discountLevelDefinitionEntries = discountLevelDefinitionTable.result.entries.findAll { it['Product Family'] instanceof Map } |
Resources for Further Learning
References
Custom Forms Configuration How-To(s)
Documentation
Groovy API
NOTE: If you have any inquiries regarding the content in this learning path, you can utilize the following online Pricefx forums: AskPricefx community or Pricefx GenAI chatbot.