Missing roll-up condition on Line items fields to deal

Hi,

I’m trying to document on the deal record (and from there also to the company) quantities of listed products.

For example:

Line item A : quantity = 10
Line item B : quantity = 20

I need to have fields on the deal for # of A = 10, # of B = 20.

We will use this to show the contractual agrrements on the company/customer level.

I can’t find a solution to roll up the quantity based on the product ID, only per the line item ID which is different in every deal.

Any idea how to solve? i would use also the name but not possible as well in the roll-up deal field.

I built this to roll up quantity per specific product but can’t use the condition as it only shows the line item record id and not the original product

Unfortunately, HubSpot’s native roll-up properties currently only allow roll-ups based on line item properties (like Line Item ID, Quantity, Price, etc.)—but not based on the associated Product ID or Product Name. That’s why you’re only seeing Line Item ID as a filter and not the actual product metadata.

:white_check_mark:Here are your current options and workarounds:

:wrench:1. Use a Custom Coded Workflow (with Operations Hub Professional or Enterprise)

You can create a custom-coded action in a workflow that:

  • Loops through all line items on a deal
  • Checks each line item’s associated product (by ID or Name)
  • Sums the quantity for each matching product
  • Updates a custom property on the deal, e.g., # of Product A, # of Product B

This Requires:

  • Operations Hub Pro+
  • Custom code knowledge (JavaScript + HubSpot APIs)

If you’d like, I can help write that code as well.

:puzzle_piece: 2. External Sync via Integromat (Make), Zapier, or Custom App

Another option: use tools like Make.com or Zapier to:

  • Trigger when a deal is updated
  • Pull all line items
  • Sum quantities per product
  • Update deal fields

This is good if you don’t want to deal with custom code directly in HubSpot.

:magnifying_glass_tilted_left:3. Naming Convention Workaround (Limited)

If you rename products with unique names and ensure no duplicates, sometimes people try to use Name as a condition—but HubSpot doesn’t expose product Name in roll-up filters natively, so this has limited success and isn’t reliable at scale.

:prohibited:Current Limitation (Why It Happens)

HubSpot doesn’t treat the product database and the quote/line items as tightly connected in the way we’d expect for relational queries. Line items are copies of product data at the time they’re added, which is why only the Line Item ID is available in filters.

:white_check_mark:Recommended

If this is a recurring business need (like showing contractual quantities at the company level), the custom-coded workflow or external automation tool is your best bet.

If you’re not sure how to build it, our agency MarketMinds Creative can help you set it up via a HubSpot automation or custom integration.

Thanks for sharing!

We don’t have Operations Hub Pro/Ent, is there another workaround you can think of?

Since you don’t have Operations Hub Pro/Ent, the best alternative would be to use external automation tools like Zapier, Make.com (Integromat), or even a lightweight custom app that connects via HubSpot’s API. These can be set up to:

  • Trigger when a deal is updated
  • Pull associated line items
  • Match them to product IDs or names
  • Calculate quantities per product
  • Update custom properties on the deal

This way, you still get the same functionality without needing Operations Hub Pro.

If setting this up sounds a bit complex, feel free to reach out — our agency MarketMinds Creative can help you implement this kind of integration smoothly.

Hi! I have Operations Hub and have been trying to attempt something like this with custom code. Good news: A value is being populated, bad news its 0. If you could review this code and tell me where I messed up, I’d much appreciate it.

Here is the background: I need to sum all line items that have ‘1850’ in the name and return it to a field on the deal record titled “Total Devices”. After the custom code action output of sum_quantity; I have an “Edit Record” action that edits the associated deal record field “Total Devices” to be the sum_quantity from the custom code below.

Here is the code i am working with - where am I wrng? (full disclosure, I am not a developer)

const hubspot = require('@hubspot/api-client');

exports.main = async (event, callback) => {
 const apiClient = new hubspot.Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN });

 // Ensure this maps to the Deal ID in your workflow input mapping
 const dealId = event.inputFields['hs_object_id'];

 const TARGET_LINE_ITEM_NAME = '1850';
 const OUTPUT_PROPERTY = 'total_devices'; // Confirm internal name in HubSpot properties

 let lineItemIds = [];
 try {
 const lineItemsResponse = await apiClient.crm.deals.associationsApi.getAll(dealId, 'line_item');
 lineItemIds = (lineItemsResponse.results || []).map(item => item.id);
 } catch (error) {
 console.error('Error fetching associated line items:', error);
 callback({ outputFields: { error: 'Failed to fetch line items' } });
 return;
 }

 if (!lineItemIds.length) {
 // If no line items, set to 0 (number)
 await apiClient.crm.deals.basicApi.update(dealId, {
 properties: { [OUTPUT_PROPERTY]: 0 }
 });
 callback({ outputFields: { sum_quantity: 0 } });
 return;
 }

 try {
 const lineItemsBatch = await apiClient.crm.lineItems.batchApi.read({
 inputs: lineItemIds.map(id => ({ id })),
 properties: ['name', 'quantity']
 });

 const total = (lineItemsBatch.results || [])
 .filter(li => li.properties.name && li.properties.name.includes(TARGET_LINE_ITEM_NAME))
 .reduce((sum, li) => sum + Number(li.properties.quantity || 0), 0);

 // Here we update the property with a NUMBER, not a string
 console.log('Updating property:', OUTPUT_PROPERTY, 'to:', total);
 await apiClient.crm.deals.basicApi.update(dealId, {
 properties: { [OUTPUT_PROPERTY]: total }
 });
 console.log('Update complete');

 callback({ outputFields: { sum_quantity: total } });
 } catch (error) {
 console.error('Error processing line items or updating deal:', error);
 callback({ outputFields: { error: 'Failed to update deal or sum quantities' } });
 }
};

Hey, @TReissman9 :waving_hand: asked our new friend Gemini # Pro to review what you shared. You were close, but I think we can get there. I’ll add the code in a separate reply. — Jaycee

Example

const hubspot = require('@hubspot/api-client');

exports.main = async (event, callback) => {
 // Make sure your secret is named exactly 'HUBSPOT_ACCESS_TOKEN' in the workflow action
 const apiClient = new hubspot.Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN });

 // 1. Get Deal ID automatically (safest method)
 const dealId = event.object.objectId;

 const TARGET_LINE_ITEM_NAME = '1850';
 const OUTPUT_PROPERTY = 'total_devices'; 

 console.log(`Starting process for Deal ID: ${dealId}`);

 try {
 // 2. Use the v4 Associations API (Modern Standard)
 // We ask for the 'deal' -> 'line_item' association
 const associationsResponse = await apiClient.crm.associations.v4.basicApi.getPage(
 'deal',
 dealId,
 'line_item'
 );

 // Check if we found any associations
 if (!associationsResponse.results || associationsResponse.results.length === 0) {
 console.log('No associated line items found. Setting value to 0.');
 await updateDeal(apiClient, dealId, OUTPUT_PROPERTY, 0);
 return callback({ outputFields: { sum_quantity: 0 } });
 }

 // 3. Extract IDs (Note: v4 uses 'toObjectId')
 const lineItemIds = associationsResponse.results.map(assoc => ({ id: assoc.toObjectId }));
 console.log(`Found ${lineItemIds.length} line items. Fetching details...`);

 // 4. Batch Read Line Item Details
 const lineItemsBatch = await apiClient.crm.lineItems.batchApi.read({
 inputs: lineItemIds,
 properties: ['name', 'quantity']
 });

 // 5. Filter and Calculate
 const total = (lineItemsBatch.results || [])
 .filter(li => li.properties.name && li.properties.name.includes(TARGET_LINE_ITEM_NAME))
 .reduce((sum, li) => sum + Number(li.properties.quantity || 0), 0);

 console.log(`Calculation complete. Total quantity for '${TARGET_LINE_ITEM_NAME}': ${total}`);

 // 6. Update the Deal
 await updateDeal(apiClient, dealId, OUTPUT_PROPERTY, total);

 // Return output (though the update is already done above)
 callback({ outputFields: { sum_quantity: total } });

 } catch (error) {
 console.error('Error:', error.message);
 // Log the full API error if available for debugging
 if (error.response) console.error(JSON.stringify(error.response.body, null, 2));
 callback({ outputFields: { error: 'Failed to process' } });
 }
};

// Helper function to handle the update
async function updateDeal(client, id, prop, value) {
 console.log(`Updating deal property '${prop}' to: ${value}`);
 await client.crm.deals.basicApi.update(id, {
 properties: { [prop]: value }
 });
}

Things to watch out for:

  • Secret Name: In your Custom Code action, under the “Secrets” tab, make sure the secret is named exactly HUBSPOT_ACCESS_TOKEN.
  • Workflow Action: Since this code updates the deal directly (via updateDeal), you can actually remove the “Edit Record” action that comes after this in your workflow! The code handles it all.

I’ll set up test in my portal and add the result here. Have fun testing! — Jaycee