Appearance
Exercise 5 (Optional): Ground a Prompt with Data 360 (Recent Orders)
In this exercise, you’ll query recent order data from Data 360 using a customer’s external identifier, then use those recent orders to ground the personalized recommendations Flex template you created in Exercise 4.
TIP
This exercise assumes you completed the Data 360 chapter through ingesting Orders and running identity resolution. If you haven’t, you can still build the Apex class, but it won’t return data until Orders are available in Data 360.
Step 1: Create an Apex class that queries Data 360 orders
In Code Builder, open the command palette (
CMD + SHIFT + Pon Mac orCTRL + SHIFT + Pon PC).Search for Apex and click Create Apex Class.
Enter GetRecentOrdersFromData360 as the class name and accept the default directory.
Replace the default code with the following code:
apexpublic with sharing class GetRecentOrdersFromData360 { @InvocableMethod(label='Get Recent Orders from Data 360') public static List<Response> getOrders(List<Request> requests) { Request input = requests[0]; Contact contact = input.contactRecord; // We join CRM -> Data 360 using the Pronto app account id (external identifier). String customerId = contact.Pronto_App_Account_Id__c; // If the contact is missing the external id, return an empty payload. if (String.isBlank(customerId)) { Response empty = new Response(); empty.Prompt = JSON.serialize(new Map<String, Object>{ 'customerId' => null, 'recentOrders' => new List<Object>() }); return new List<Response>{ empty }; } // Query recent orders from Data 360. // Note: If your Orders DLM uses different field API names, update the SQL accordingly. ConnectApi.CdpQueryInput queryInput = new ConnectApi.CdpQueryInput(); queryInput.sql = 'SELECT Order_id__c, Order_Date__c, Order_Status__c, Restaurant_Name__c, Total_Amount__c, Item_Count__c, Items_Ordered__c, Delivery_City__c, Time_Of_Day_Segment__c, Promotion_Flag__c, Order_Rating__c, Feedback_Comments__c ' + 'FROM Orders__dlm ' + 'WHERE Customer_Id__c = \'' + String.escapeSingleQuotes(customerId) + '\' ' + 'ORDER BY Order_Date__c DESC ' + 'LIMIT 5'; ConnectApi.CdpQueryOutputV2 response = ConnectApi.CdpQuery.queryAnsiSqlV2(queryInput); List<Object> orders = new List<Object>(); Map<String, Integer> restaurantCounts = new Map<String, Integer>(); Decimal totalSpend = 0; Integer orderCount = 0; Object mostRecentOrderDate = null; if (response != null && response.data != null) { for (ConnectApi.CdpQueryV2Row row : response.data) { // row.rowData aligns to the SELECT list above (0-based). Map<String, Object> order = new Map<String, Object>{ 'orderId' => row.rowData.size() > 0 ? row.rowData[0] : null, 'orderDate' => row.rowData.size() > 1 ? row.rowData[1] : null, 'orderStatus' => row.rowData.size() > 2 ? row.rowData[2] : null, 'restaurantName' => row.rowData.size() > 3 ? row.rowData[3] : null, 'totalAmount' => row.rowData.size() > 4 ? row.rowData[4] : null, 'itemCount' => row.rowData.size() > 5 ? row.rowData[5] : null, 'itemsOrdered' => row.rowData.size() > 6 ? row.rowData[6] : null, 'deliveryCity' => row.rowData.size() > 7 ? row.rowData[7] : null, 'timeOfDaySegment' => row.rowData.size() > 8 ? row.rowData[8] : null, 'promotionFlag' => row.rowData.size() > 9 ? row.rowData[9] : null, 'orderRating' => row.rowData.size() > 10 ? row.rowData[10] : null, 'feedbackComments' => row.rowData.size() > 11 ? row.rowData[11] : null }; orders.add(order); // Lightweight rollups to make the prompt easier to use. Object restaurantNameObj = order.get('restaurantName'); if (restaurantNameObj != null) { String rn = String.valueOf(restaurantNameObj); restaurantCounts.put(rn, (restaurantCounts.containsKey(rn) ? restaurantCounts.get(rn) : 0) + 1); } Object amtObj = order.get('totalAmount'); if (amtObj != null && String.valueOf(amtObj) != '') { try { totalSpend += Decimal.valueOf(String.valueOf(amtObj)); orderCount += 1; } catch (Exception e) { // ignore parse issues } } if (mostRecentOrderDate == null) { mostRecentOrderDate = order.get('orderDate'); } } } Response res = new Response(); res.Prompt = JSON.serialize(new Map<String, Object>{ 'customerId' => customerId, 'recentOrders' => orders, 'mostRecentOrderDate' => mostRecentOrderDate, 'topRestaurants' => restaurantCounts, 'avgOrderValue' => (orderCount > 0 ? (totalSpend / orderCount) : null) }); return new List<Response>{ res }; } public class Request { @InvocableVariable(required=true) public Contact contactRecord; } public class Response { @InvocableVariable public String Prompt; } }Save the file (
CMD + Son Mac orCTRL + Son PC).In the file explorer (left sidebar), click the GetRecentOrdersFromData360.cls-meta.xml file.
Update the class API version to 60.0. The file should now look like this:
xml<?xml version="1.0" encoding="UTF-8"?> <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>60.0</apiVersion> <status>Active</status> </ApexClass>Save the file.
Right-click anywhere in the code and select SFDX: Deploy This Source to Org.
Step 2: Ground the Flex template with recent orders from Data 360
In Setup, open Prompt Builder.
Open the Generate Customer Recommendations template.
In the Prompt Template Workspace, add a new section under the existing context:
txtRecent order context (from Data 360):On the line after "Recent order context (from Data 360):", use the Insert Resource search box and select: Apex > GetRecentOrdersFromData360.
Update the prompt instructions to use recent orders when available. For example:
txtIf recentOrders are provided, use them to personalize recommendations: - Avoid recommending the same restaurant name the customer ordered from most recently (use recentOrders.restaurantName) - Suggest variety across storefront types and cuisines when possible - Prefer items that are Available__c in the grounded menu data - If recentOrders is empty, rely on Favorite Cuisine and the storefront details provided (including ratings when available)Click Save As > Save as New Version.
In the Preview panel, select Alex Morgan as the Contact.
Click Preview.
- In the Resolved Prompt panel, confirm the Data 360 query output is included.
- In the Generated Response panel, confirm the recommendations reference recent order context when it is present.
Summary
In this exercise, you grounded a Flex template with recent Data 360 order context using Invocable Apex and a Data 360 query keyed off Contact.Pronto_App_Account_Id__c.