How to set events and logic to process events
The following How to cover the real-time processing for the Price List. We want to store PL information in the Data Source in real-time. The old solution was, that we have several Dataloads runs every 10 mins which checks if there is some updated PL from the last run in special status APPROVED/REVOKED/SUPERSEDED. Based on it we process some data loads and updates in DS. But it is not effective, we wanted to have real-time solution. This article will show all the steps that need to be done.
Platform Manager - Event allowance setup
You need to have privileges to be able to admin the events.
In the PM Account → Partitions → Partition Overview → Event Admin
Click to button Event Configurator
Choose what events you would like to catch and process
Leave Event to Kafka/MQ = PROD (it is the setting for the dev/qa/prod the same) , you will specify later how and where the events will be matched and processed
Event URL = localhost
Ensure that events are generated and visible in the Event list. To be able manage PL we set events fro the ITEM_APPROVED_PL and ITEM_PL_REVOKE.
If events are generated and visible you can continue with the following steps. If you can’t see the events in the list contact support and ask for help.
Partition - Groovy Logic
There is the limitation of what type of logic can be used for the event processing. Currently, you can have:
Generic Logic - only Generic Logic
Data Load - only Data Load logic
Calculation - only CFS
There is a Jira ticket to extend Calculation with CF logic (https://pricefx.atlassian.net/browse/PFIM-7326 )
The second limitation is that only Generic logic can read the parameters sent by events. The Data load is blind.
It means when we catch the PL approval event, we want to know what PL was approved. The Generic Logic can read inputs from events, but Data Load logic can’t see anything.
There is a Jira ticket to extend Data Load to be able to pass parameters into Data Load https://pricefx.atlassian.net/browse/PFUN-24978
The biggest problem for us was the situation when we were able to react to the event, but if we wanted to process it by Data Load (requirement), the DL was blind and the logic didn’t know what PL was approved. We were able to read inputs in Generic Logic, but we were not able to run the Data Load (it is not allowed to use actionBuilder). We would like to use CF logic, but it was not possible to join with the event. Many show-stoppers… Therefore several Jira tickets were created to have better options how to process events and the most common case - process them via Data Load which knows the object ID.
Solution
We created a Generic Logic, which was able to read the event parameters and used boundcall to input rows to the Data Source.
The event parameters from ITEM_APPROVED_PL that is processed is:
{
"data": [
{
"version": 15,
"typedId": "531.PL",
"targetDate": "2023-10-31",
"label": "New Price List retest_2 TK 26(no data in PP.GPR List Price From RRP)",
"locale": "en",
"calculationMessages": "[]",
"numberOfItems": 6,
"keepManualOverrides": false,
"writeOnlyChangedItems": false,
"configuration": "{\"elementNames\":[\"Error\",\"IsHeroSKU\",\"FinalRRPLC\",\"Market\",\"StandardCostEstDeltaPct\",\"HistoryTrend\",\"PricingDependency\",\"SalesDetail\",\"FinalFloorPriceDeltaPct\",\"RoundedRRPLC\",\"PricePointRRPLC\",\"TargetNetPrice\",\"FinalRRPEUR\",\"ManualRRPLC\",\"FinalSalesUnitListPriceEUR\",\"GrossMarginPctTarget\",\"LatestRRPEUR\",\"ManualSalesUnitListPriceLC\",\"UsedRule\",\"ProposedRRPLC\",\"FinalFloorPrice\",\"GrossMarginPctDiff\",\"NetSalesValue12mEUR\",\"FinalTargetNetPrice\",\"GrossMarginPctAtTargetNetPrice\",\"TPS2NetVTargetNetPricePct\",\"ProductMasterData\",\"PricingHierarchyBasedRule\",\"LatestRRPLC\",\"FinalRRPLCDeltaPct\",\"ManualFloorPrice\",\"FinalTargetNetPriceDeltaPct\",\"SalesUnitListPriceLC\",\"Brand\",\"AAOptimizedSuggestionRule\",\"FloorPrice\",\"FinalSalesUnitListPriceLC\",\"MinMarginRule\",\"AAOptimizedDetail\",\"GrossMarginPct12m\",\"ManualTargetNetPrice\",\"NetSalesValue12mLC\",\"GrossMarginPctEst\",\"CompetitorBasedRule\",\"FinalSULPLCDeltaPct\",\"GrossMarginPctAtFloorPrice\",\"MarketPriceDetail\",\"LeadMarketBasedRule\",\"UsedRounding\",\"LocalRRPVGlobalRRPPct\",\"Currency\",\"MarketPriceAVG30DaysLC\",\"ProductHierarchy1\",\"ProductLine\"],\"hiddenElementNames\":[],\"productFilterCriteria\":{\"_constructor\":\"AdvancedCriteria\",\"operator\":\"and\",\"criteria\":[{\"fieldName\":\"sku\",\"operator\":\"inSet\",\"value\":[\"1000614\",\"1000661\",\"1000812\",\"1003817\",\"1014418\",\"1017244\",\"1017342\",\"1001705\",\"1014794\",\"1026575\",\"1057536\",\"1000581\"]}]},\"resultElementName\":\"FinalRRPEUR\",\"shotgunModeEnabled\":false,\"defaultFormulaOverride\":\"PL_GPR\",\"matrixFormulaName\":\"PL_MatrixGPR\",\"matrixElementName\":\"Markets\",\"preferencesSource\":\"530\",\"plTypeTypeId\":\"3.PLPGTT\",\"formulaParameters\":{\"CreatorSalesOrg\":[\"JP30\",\"SAPInt\",\"DE30\",\"SE31\",\"DK30\",\"FinMgr\",\"GB31\",\"FI32\",\"GPR\",\"ProdMgr\",\"KAM\"],\"GPRConfigurator\":{\"markets\":[\"FI_TEST6\",\"NO\",\"FI_TEST2\",\"DE\",\"FI_TEST4\"],\"marketMatrix\":[{\"market\":\"FI_TEST6\",\"fromDate\":\"2023-12-01\",\"selected\":true},{\"market\":\"NO\"},{\"market\":\"FI_TEST2\",\"fromDate\":\"2023-31-31\",\"selected\":false},{\"market\":\"DE\"},{\"market\":\"FI_TEST4\"}]}},\"notifyWhenFinished\":\"NONE\",\"headerInputs\":[]}",
"errorMode": "ABORT",
"pricelistType": "MATRIX",
"approvalState": "APPROVED",
"workflowStatus": "NO_APPROVAL_REQUIRED",
"hasCriticalAlert": false,
"submittedByName": "bretislav.kupera",
"integrationStatus": "REVOKED",
"createdByName": "phan.huy",
"lastUpdateByName": "bretislav.kupera",
"updatedBy": 2,
"approvedByName": "bretislav.kupera",
"updateDate": "2024-05-17",
"createDate": "2023-10-31T03:57:41",
"createdBy": 11,
"lastUpdateDate": "2024-05-17T08:57:04",
"lastUpdateBy": 2,
"status": "READY",
"calculationStartDate": "2023-10-31T03:58:06",
"calculationDate": "2023-10-31T03:58:08",
"approvalDate": "2024-05-22T09:50:07",
"headerTypeUniqueName": "GPR",
"submitDate": "2024-05-22T09:50:07",
"id": 531
}
],
"metricName": "PL_Approved",
"eventType": "ITEM_APPROVED_PL"
}
The generic logic can read the event parameters in the element PriceListId from the data input.
input.data?.getAt(0)?.get('typedId')
then we simply prepare data for the Data Source insert:
String sku = 'RevokedX-' + sharedId //id for the PL
String market = 'MMX-' + input.eventType // event type read from the event JSON
def DS = [
PRICE_LIST_ID : "",
CUSTOMER_ID : "",
MATERIAL : "",
CONDITION_TYPE : ""
]
def payload = [
data: [
header : DS.keySet(),
options: [direct2ds: true],
data : [
[
sku ,
market ,
"test:" + sharedId ,
"ZFR"
]
]
]
]
String jsonBody = api.jsonEncode(payload)
def response = api.boundCall("localhost", "datamart.loaddata/Test_Event_PL_Revoke", jsonBody, false)
This solution covers the case of what we need. React in real-time on the event when Price List is approved or revoked (we don’t need to have an post-approval step and batch runs every 10 mins to check if some PL was updated and what is the status of that PLs).
The current list of the events is here: Events
Platform Manager - event setting and connect to the logic
When the partition generates the proper events and you have a logic ready to process the triggered event you just need to do the final setting.
You need to have privilege to set Event Workflow - ask support for that or ask for admin :)
If you have access, press the button “Create Event Workflow”
Enable it
Fill Name and Description
Execution order - if you have one Event catch then keep it empty. If you have more, you can setup the order here.
Then add Listener
Listener - General
Choose the name - It is fine to keep the event name or something self descripted
Description
Timeout
Listener - Source
Add the Source Event - don’t combine more events in one listener if not necessary. You can have some more complex setting, but this is not stable and can happened that some events will be missed, because the catcher will wait for several events and tries to combine them, etc… So keep it simple. Create separate Event Workflow for each event you want to process, don’t combine events in one Workflow if you don’t have very good reason for it.
Select the Source Type = PARTITION - you want to catch events on the partitions
Select the Source Name = <your partition>. Remember the Event to Kafka/MQ = PROD in the initial setting? Here is the partition differentiator, so don’t be confused, that originally you set PROD
Then choose what event you want to catch - ITEM_APPROVED_PL in this case
Listener - Action
Here you set on which partition the event will be processed. Honestly I didn’t try to trigger event in DEV and process it in QA But it seems as the theoretical option here. Anyway, my solution was ALL in ONE partition
Select Target Type = PARTITION
Target Name = <your partition>
Action Type - here you can choose what logic will process the event. As mentioned in the beginning with the limitation of the logic types. Our solution uses Generic Ligic = LOGIC
Action ID - the name of the logic
And that’s it. As recommended, create Event Workflow for each event, don’t combine them in the one workflow (when we did it, we have a problem with the loosing of the events and they were not processed, because the setting causes some waiting and mutual dependency, etc…)
So if we want to catch PL approval and revoke we have two Event workflows defined:
Then test the event. Trigger the event, check if the Last Run was updated, it means the event was catched and processed. And hopefully you will also have the final required result in the DS.
Found an issue in documentation? Write to us.