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