1/**
2 * @description :
3 * This Hook is written to add support for ESM's ReliesOn Relationships APIs on CPQ Cart
4 * In this class we are writing hook on postCartsItems.PostInvoke and deleteCartsItems.PreInvoke
5 * postCartsItems.PostInvoke => it covers services like, finding relationships that are eligible to be added based on XLI added to the cart
6 * it also links the qualified relationhips and discards the relationships which are already added
7 * it also does the verification if the Relationship's max cardinality is violated, only links when its not violated
8 *
9 * deleteCartsItems.PreInvoke => this hook is used to delete the linked relationship if the corresponding XLI's relationship exits and is deleted
10 *
11 * @author : chandel.gupta@salesforce.com
12 */
13global with sharing class CPQAppHandlerHookImplementation implements vlocity_cmt.VlocityOpenInterface
14{
15 private vlocity_cmt.VOIInvoker invoker = vlocity_cmt.VOIInvoker.getInstance();
16 private vlocity_cmt.VlocityOpenInterface appHandler = (vlocity_cmt.VlocityOpenInterface)invoker.invoke('B2BCmexAppHandler', 'debugCreate', null, null, null);
17
18 global Boolean invokeMethod(String methodName, Map<String, Object> input, Map<String, Object> output, Map<String, Object> options)
19 {
20 try
21 {
22 System.debug('CPQAppHandlerHook method ' + methodName);
23
24 if(methodName == 'postCartsItems.PostInvoke')
25 {
26 postCartsItemsPostInvoke(input,output,options);
27 }
28 else if(methodName == 'deleteCartsItems.PreInvoke')
29 {
30 deleteCartsItemsPreInvoke(input,output,options);
31 }
32
33 return true;
34 }
35 catch (Exception ex)
36 {
37 System.debug('--- Exception: ' + ex.getMessage());
38 System.debug('--- Stack Trace: ' + ex.getStackTraceString());
39 throw ex;
40 }
41 }
42
43 public void deleteCartsItemsPreInvoke(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options)
44 {
45 System.debug('deleteCartsItemsPreInvoke method ');
46 Map<String, Object> inputDelRel = new Map<String, Object>();
47 Map<String, Object> outputDelRel = new Map<String, Object>();
48 Map<String, Object> optionsDelRel = new Map<String, Object>();
49
50 String cartId = (String)input.get('cartId');
51 String lineItemToDel = (String)input.get('id');
52 String methodName = 'deleteOrderItemRelationships';
53
54 inputDelRel.put('cartId', cartId);
55 inputDelRel.put('lineItemId', lineItemToDel);
56
57 //remote action invocation
58 //vlocity_cmt.B2BCmexAppHandler appHandler = new vlocity_cmt.B2BCmexAppHandler();
59 appHandler.invokeMethod(methodName, inputDelRel, outputDelRel, optionsDelRel);
60 System.debug(JSON.serialize(outputDelRel));
61 }
62
63 public void postCartsItemsPostInvoke(Map<String, Object> input, Map<String, Object> output, Map<String, Object> options)
64 {
65 System.debug('postCartsItemsPostInvoke method ');
66
67 Map<String, Object> inputRel = new Map<String, Object>();
68 Map<String, Object> outputRel = new Map<String, Object>();
69 Map<String, Object> optionsRel = new Map<String, Object>();
70
71 String cartId = (String)input.get('cartId');
72 String methodName = 'getCartProductRelationship';
73
74 inputRel.put('cartId', cartId);
75 //inputRel.put('source', 'ENTERPRISE_CART');
76 //inputRel.put('rootItemId', null);
77 //inputRel.put('getOnlyRootItems', 'true/false');
78
79 //remote action invocation
80 //vlocity_cmt.B2BCmexAppHandler appHandler = new vlocity_cmt.B2BCmexAppHandler();
81 appHandler.invokeMethod(methodName, inputRel, outputRel, optionsRel);
82 System.debug(JSON.serialize(outputRel));
83
84 Map<Id, OrderItem> xlisMap = new Map<Id, OrderItem>([SELECT Id, vlocity_cmt__AssetReferenceId__c, vlocity_cmt__Product2Id__c, vlocity_cmt__Product2Id__r.Name FROM OrderItem WHERE Order.Id = :cartId]);
85 List<vlocity_cmt__ProductRelationship__c> prodRelRules = (List<vlocity_cmt__ProductRelationship__c>) outputRel.get('ProductRelationship');
86
87
88 // map to hold record of existing quantity of the relationships that are already linked
89 Map<String, Integer> existingOPRelQtyMap = new Map<String, Integer>();
90
91 Map<String,vlocity_cmt__OrderItemRelationship__c> orderProdRelsMap = new Map<String,vlocity_cmt__OrderItemRelationship__c>();
92
93 //converting List of XLI Relationships to Map, and forming Map that holds quantity of relationships that are already added
94 for(vlocity_cmt__OrderItemRelationship__c opr : (List<vlocity_cmt__OrderItemRelationship__c>)outputRel.get('OrderProductRelationship'))
95 {
96 orderProdRelsMap.put(opr.vlocity_cmt__OrderItemId__c + ';' + opr.vlocity_cmt__RelatedOrderItemId__c, opr);
97 existingOPRelQtyMap.put(opr.vlocity_cmt__OrderItemId__c + ';' + opr.vlocity_cmt__RelatedOrderItemId__r.Product2Id + ';' + opr.vlocity_cmt__ProductRelationshipIdentifier__c, (opr.vlocity_cmt__RelatedOrderItemId__r.Quantity).intValue());
98 }
99 System.debug(JSON.serialize(orderProdRelsMap));
100 System.debug(JSON.serialize(existingOPRelQtyMap));
101
102 Map<Id,List<SObject>> xliToRelProdListMap = new Map<Id,List<SObject>>();
103 Map<Id, Object> relProdIdToProdRelMap = new Map<Id, Object>();
104
105 // map to hold record of the quantity of the relationships that are to be linked
106 Map<String, Integer> toBeLinkedOPRelQtyMap = new Map<String, Integer>();
107 Map<String, String> orderItemRelProdToRuleIdMap = new Map<String, String>();
108
109 for(Id xliId : xlisMap.keySet())
110 {
111 OrderItem xli = (OrderItem) xlisMap.get(xliId);
112 for(vlocity_cmt__ProductRelationship__c prodRelRule : prodRelRules)
113 {
114 // forming "One to Many" relationship between XLI and Related Products
115 if(xli.vlocity_cmt__Product2Id__c == prodRelRule.vlocity_cmt__Product2Id__c)
116 {
117 if(xliToRelProdListMap.get(xli.Id) != null)
118 {
119 List<SObject> prodRelRuleList = (List<SObject>) xliToRelProdListMap.get(xli.Id);
120 prodRelRuleList.add(prodRelRule);
121 }
122 else
123 {
124 List<SObject> prodRelRuleList = new List<SObject>();
125 prodRelRuleList.add(prodRelRule);
126 xliToRelProdListMap.put(xli.Id, prodRelRuleList);
127 }
128
129 // map to hold record of the quantity of the relationships that are to be linked => will be used to validate cardinality violation
130 toBeLinkedOPRelQtyMap.put(xliId + ';' + prodRelRule.vlocity_cmt__RelatedProductId__c + ';' + prodRelRule.Id, 1);
131 orderItemRelProdToRuleIdMap.put(xliId + ';' + prodRelRule.vlocity_cmt__RelatedProductId__c, prodRelRule.Id);
132 }
133
134 // forming relationship between RelatedProduct and Related XLI to be used for Linking XLIs
135 if(xli.vlocity_cmt__Product2Id__c == prodRelRule.vlocity_cmt__RelatedProductId__c )
136 {
137 relProdIdToProdRelMap.put(prodRelRule.vlocity_cmt__RelatedProductId__c, new Map<String, Object>
138 {
139 prodRelRule.Id => new List<Map<String, Object>>{
140 new Map<String, Object>{
141 'id' => xli.Id,
142 'assetReferenceId' => xli.vlocity_cmt__AssetReferenceId__c,
143 'relatedProductId' => prodRelRule.vlocity_cmt__RelatedProductId__c
144 }
145 }});
146 }
147 }
148 }
149
150 // return if no related lineitem to link to in the cart
151 if(relProdIdToProdRelMap.size() <= 0) return;
152
153 // forming map "maxCardinalityMap" with key and value as => "orderItemId;relatedProdId;ruleId => max_qty", to know the max cardinality the rule supports
154 // e.g 802xx000001ncoeAAA;01txx0000006i8wAAA;a3Sxx0000004C92EAE => 6
155 Map<String, Integer> maxCardinalityMap = new Map<String, Integer>();
156
157 Map<String, List<Object>> lineItemListOfLinkagesMap = new Map<String, List<Object>>();
158
159 for(Id xliId : xliToRelProdListMap.keySet())
160 {
161
162 List<vlocity_cmt__ProductRelationship__c> xliRelProdList = (List<vlocity_cmt__ProductRelationship__c>) xliToRelProdListMap.get(xliId);
163
164 for(vlocity_cmt__ProductRelationship__c xliRelProd : xliRelProdList)
165 {
166 if(relProdIdToProdRelMap.get(xliRelProd.vlocity_cmt__RelatedProductId__c) != null)
167 {
168 if(lineItemListOfLinkagesMap.get(xliId) != null)
169 {
170 List<Object> xliLinkagesList = (List<Object>) lineItemListOfLinkagesMap.get(xliId);
171 xliLinkagesList.add(relProdIdToProdRelMap.get(xliRelProd.vlocity_cmt__RelatedProductId__c));
172 }
173 else
174 {
175 List<Object> xliLinkagesList = new List<Object>();
176 xliLinkagesList.add(relProdIdToProdRelMap.get(xliRelProd.vlocity_cmt__RelatedProductId__c));
177 lineItemListOfLinkagesMap.put(xliId, xliLinkagesList);
178 }
179 }
180
181 //Map<String, Object> relationshipMap = (Map<String, Object>) relationship;
182 String orderItemId = xliId;
183 String relatedProdId = (String) xliRelProd.vlocity_cmt__RelatedProductId__c;
184 String ruleId = (String) xliRelProd.Id;
185 Decimal maxQuantity = (Decimal) xliRelProd.vlocity_cmt__MaxQuantity__c;
186
187 maxCardinalityMap.put(orderItemId + ';' + relatedProdId + ';' + ruleId, maxQuantity.intValue());
188 }
189 }
190
191 System.debug(JSON.serialize(xliToRelProdListMap));
192 System.debug(JSON.serialize(relProdIdToProdRelMap));
193 System.debug(JSON.serialize(lineItemListOfLinkagesMap));
194
195 System.debug(JSON.serialize(maxCardinalityMap));
196 System.debug(JSON.serialize(existingOPRelQtyMap));
197 System.debug(JSON.serialize(toBeLinkedOPRelQtyMap));
198 System.debug(JSON.serialize(orderItemRelProdToRuleIdMap));
199
200 // linking LineItems based on defined "ReliesOn" relationships
201 for(Id xliId : lineItemListOfLinkagesMap.keySet())
202 {
203 Map<String, Object> linkItemInput = new Map<String, Object>();
204 Map<String, Object> linkItemOutput = new Map<String, Object>();
205
206 for(Object xliLinkages : lineItemListOfLinkagesMap.get(xliId))
207 {
208 Id linkedRelXliId = (Id) ((Map<String, Object>)((List<Object>) JSON.deserializeUntyped((String)JSON.serialize(((Object) ((Map<String, Object>) JSON.deserializeUntyped((String)JSON.serialize(xliLinkages))).values().get(0))))).get(0)).get('id');
209 Id linkedRelProdId = (Id) ((Map<String, Object>)((List<Object>) JSON.deserializeUntyped((String)JSON.serialize(((Object) ((Map<String, Object>) JSON.deserializeUntyped((String)JSON.serialize(xliLinkages))).values().get(0))))).get(0)).get('relatedProductId');
210 Id ruleId = orderItemRelProdToRuleIdMap.get(xliId + ';' + linkedRelProdId);
211
212 // Add relationship only when relationship is not already linked also check if the Rule's max cardinality is violated or not,
213 // link only when it's not violated, else throw soft warning message in the response
214 if(!(orderProdRelsMap.get(xliId + ';' + linkedRelXliId) != null))
215 {
216 String key = xliId + ';' + linkedRelProdId + ';' + ruleId;
217 Integer maxCardinality = maxCardinalityMap.get(key) == null ? 0 : maxCardinalityMap.get(key);
218 Integer existingCardinality = existingOPRelQtyMap.get(key) == null ? 0 : existingOPRelQtyMap.get(key);
219 Integer toBeAddedCardinality = toBeLinkedOPRelQtyMap.get(key) == null ? 0 : toBeLinkedOPRelQtyMap.get(key);
220
221 System.debug('maxCardinality => ' + maxCardinality + ' existingCardinality => '+ existingCardinality + 'toBeAddedCardinality => ' + toBeAddedCardinality);
222
223 if(existingCardinality + toBeAddedCardinality > maxCardinality)
224 {
225 System.debug('Max cardinality limit for the rule : ' + ruleId + ' is violated for OrderItem : ' + xliId + ' and Related OrderItem' + linkedRelXliId);
226 vlocity_cmt.JSONResult result = (vlocity_cmt.JSONResult) output.get('result');
227
228 String message = 'Max cardinality limit for the rule : ' + ruleId + ' is violated for OrderItem : ' + xliId + ' and Related OrderItem : ' + linkedRelXliId;
229 //vlocity_cmt.JSONMessage jsonMessage = new vlocity_cmt.JSONMessage('422', vlocity_cmt.JSONMessage.Severity.WARN, message);
230 //result.messages.add(jsonMessage);
231 }
232 else
233 {
234 System.debug('Qualified to be added : ' + xliId + ';' + linkedRelXliId );
235 Map<String, Object> lineItemLinkageMap = new Map<String, Object>{xliId => xliLinkages};
236
237 // Setting remote parameters
238 linkItemInput.put('cartId', cartId);
239 linkItemInput.put('LineItemLinkage', lineItemLinkageMap);
240
241 //remote action invocation
242 appHandler.invokeMethod('linkProductsForOrder', linkItemInput, linkItemOutput, new Map<String, Object>());
243 }
244 }
245 else {
246 System.debug('Disqualified to be added, as relationship already exists : ' + xliId + ';' + linkedRelXliId );
247 }
248 }
249 }
250 }
251}