Summarising data from related records (Using JSON Calcs)

I have 3 apps (invoices, contracts and accounts).

The invoice app has an outgoing relation to contracts. Contracts has an outgoing relation to accounts.

In the accounts app, I’m trying to make a table that summarises related contracts and invoices.

I’m struggling to get the related invoices to be listed correctly beneath my contracts. The screenshot below shows what I’m trying to achieve. I think I need to create an array of contract with related invoices but not sure how.

Screenshots of Accounts App


Sreenshots of Contracts App




Screenshots of Invoices App



Not sure if you can access this or not

Public access

Hi Richard,

I understand that summarizing data spread out across multiple apps with complex relationships can be challenging. For myself, I can tell you that I’ve had an incredible amount of success by creating a ‘JSON’ field in every single app. This field is always hidden and produces a JSON object of the record, with all of the formatting already completed. I feel way more confident about Tape’s calculation field than I do Podio’s, therefore, these fields are incredible tools to summarize data, as well as summarizing related data as well. Therefore, when you collect this data for calculation fields, all you have to do is use @All of JSON and you have everything at once in one handy token.

Also, once you build these JSON fields, when the time comes to build the actual summary calc, you can then just pop each bit of individual JSON into an AI tool like Gemini and Claude and say “Given this JSON, write me the Javascript required to convert to markdown or HTML.” Ever since I learned this method, I’ve spent exactly 0 minutes building summary calcs, because AI nails it every time once the JSON is clear and in place.

1 Like

Hi Andrew, just so I get this right, your ‘JSON’ field is in a multi-line text field with data extracted using the payload action that you showed me in the previous comment?

No, I am storing JSON in a calculation field that gathers data from all required sources, and then just returns a JSON.stringify() statement at the end. It has to be inside of a try/catch block or it won’t save.

try {
  JSON.stringify({
    id: ï»ż@Record IDï»ż,
    creation_date: @Creation Date,
    status: @Status,
    start_time: ï»ż@Start Timeï»ż != null ? moment(ï»ż@Start Timeï»ż).tz('America/New_York').format('YYYY-MM-DD[Z]HH:mm:ss') : null,
    ... etc., additional fields as required...
  })
} catch (e) {
  e  // Will return the error message which is great for testing, but in production, you might consider returning a blank object if you're using this in other calc fields
}

I’m still not sure if the below method is totally the best way to do this, but it works. When you’re fetching this JSON from another calc field, you have to go through an extra step to ensure that the JSON is parsed correctly, and not included if it doesn’t. This way, you can get a final JSON object that actually contains related data.

var raw_entries = ï»ż@All of JSONï»ż; // Gathered from the first step above

var cleansed_entries = [];

for (var i = 0; i < raw_entries.length; i++) {
  var entry = raw_entries[i];

  try {
    var obj = typeof entry === 'string' ? JSON.parse(entry) : entry;
    cleansed_entries.push(obj);
  } catch (e) { 
    // Skip malformed entry
  }
}

// Example of sorting related items before installing
cleansed_entries.sort(function(a, b) {
  return new Date(a.creation_date) - new Date(b.creation_date); 
});

try {
  JSON.stringify({
    id: ï»ż@Record IDï»ż,
    related_entries: cleansed_entries
  })
} catch (e) {
  e // Or blank object as per above
}

Once you have these fields in place, you can then just reference this field in another calc and use the raw JSON object (cleansed first, as per the above method) and convert it to HTML, markdown, or whatever format you need. AI is exceptionally helpful in doing this, particularly if you can feed it clear examples of what you’re looking for. ES6 Javascript is king and AI has it pretty much perfected.

PS: One of the main challenges doing this in Tape is the fact that you can’t see a live preview using an actual item. Therefore, if you try to add these fields to an app with a LOT of records in them, it might cause issues if you have to save it multiple times. Also, it’s important to be aware that Tape calculation fields actually produce webhooks when they change, so if you have an automation that fires “on update” but it happens on any field, that flow will fire on items where the calc field has been updated and saved. Long story short, extreme caution is recommended.

4 Likes

Another thing about this method is that you get to control all of the business logic of how a parameter should be formatted directly within the source itself. If it ever has to change, you only have to change it in one place.

Using multiple tokens to draw related items, rebuild them, sort and reformat them, has always seemed cumbersome to me. Now I can ensure my returned JSON object is perfectly formatted and when I draw it into another calc, I can just focus on how I want to display it, making those calcs much simplier, easier to read and (hopefully) more efficient.

1 Like

Actually, I just thought of one more as well.

When you’re collecting items via workflows, you no longer have to concern yourself with each individual field, you can just go after the JSON field for all of your needs. And if you fetch records via the Tape API SDK, use jsonata to get what you need fast, you can write entire robust workflows that are completely encapsulated in an Execute Script function, and after giving it a bit of information, ask AI to write the whole workflow for you. While Tape does not allow you to copy and paste workflows in between different apps, copying massive code blocks is supremely easier.

JSON is the key to everything and AI eats it for breakfast, lunch and dinner.

4 Likes

Hi Andrew,

So far so good, but how do I convert the string that is obtained to raw JSON as the command JSON.rawJSON doesn’t function.

Thanks in advance

If I understand you correctly


That code for the loop is for gathering multiple items and creating a JSON object from the results. So if you’re asking, now that I have this JSON summary field, how can I create another calc field and references that JSON calc, the answer is, you still have to parse it, but it doesn’t require a loop.

Imagine I have a field called ‘JSON’ in an app, and then I create another calc field in the same app called “Project Summary” where my table is going to be.

The start of my “Project Summary” calc would look like this.

var raw = ï»ż@JSONï»ż
var html = ""

// Parse the JSON string
var json = {};

try {
  json = JSON.parse(raw);
} catch (e) {
  // If parsing fails, do something here so that your code can respond, perhaps a blank object where you can check for a parameter there and respond to it being null
  
  };
}

If I was referring to the JSON from your screenshot, I could then use json.related_entries to access those.

1 Like

In the case of a single related item, you could also use @All of JSON[0] where it’s the JSON field from that related item. Basically, you either need to loop through for parsing, or just parse a single collected object.

Great - thanks. Have a nice evening.

1 Like

Hi Andrew,

Thanks for all the help - you are right, this is clearly the way to go to making quick summary tables. However, I’m struggling with the accessing of the data.

So this is my cleansed data object, that is put back into a string (otherwise Tape doesn’t allow the field to save).

If I have understood properly, in the project summary field of the same app, I would need to JSON.Parse() it to get it back into an object, right?

But then how would you write the full dot notation to access “Invoice 3” for example. Nothing I do works, so clearly I’m missing something.

A couple of thoughts.

Looking at your example JSON, when you say “access Invoice 3”, you’re only really including the name of the invoice in your “invoice_list” array. Therefore, if you want more information about that invoice, you would need to include it all in the JSON as it’s being built. Each invoice would have its own JSON object field, and then as you draw in invoices from another app, you can ensure you can include the entire bit of information you need.

The beginning of your calc would look like the single parsed object I state above. You can test your calc to just try and access any single parameter to ensure that it works. Once it does, my recommendation is to pop everything else into Claude and say “How do I get what I need?”

If you wanted to access “Invoice 3”, the best plan might be to loop through the values of invoice_list and then use if/then statements to pull out what you want. Again, AI can be helpful here if you just clearly explain what the end outcome should be and work your way backwards from there.

OK, thanks - I will take a look at Claude. I think my problem is just not knowing how to code properly for accessing the object.

I have understood that if I want more information regarding a record, I would need to include it in the JSON field. I’m just playing around with test apps for now to get the approach right.

1 Like

It’s just difficult to answer because I would need to know more about what you’re trying to display. However, once you get all the JSON set up, again, if you just feed it into Claude or Gemini or ChatGPT and say “I want a table or something that looks like this, and I want this data point here, etc.”, you should be able to get what you need.

Hi Andrew,

Through some trial and error, I have realised that the JSON being generated throws an error, whereas the manually entered raw string produces the correct result. Can you see what I’m doing wrong in constructing the JSON from related fields?






It’s a bit hard to decipher, both what you’re trying to achieve and what the errors might be. Where you are in the apps in the screenshots doesn’t make sense to me. “Does work” and “Doesn’t work” isn’t a lot to go on either.

In your invoices app, I would have a JSON field encapsulating everything about the invoice. Then, wherever you want to collect invoices, you would use @All of Invoice JSON, filter through each one to cleanse them, and then build your related array using the cleansed items, as I have shown above.

In that JSON field that is collecting and summarizing invoices, you can then use @JSON and then cleanse it using something like:

// Parse the JSON string

var json = {};

try {

 json = JSON.parse(raw);

} catch (e) {

 // If parsing fails, create empty structure

 json = {

 };

}

I would really need to see a lot more info to assist further. Once again, Claude is super helpful, particularly if you feed it examples of what you’re doing and what you already have.

Hi Andrew, thanks for trying to help. This has become quite a long thread and may be getting a bit confusing. I will try to cut a long story short.

Following your steps I get a final JSON object that I want to extract data from to put in a table or whatever

Here is the under the hood workings of the field with the JSON showing that I have ‘cleansed’ the JSON.

So far, so good.

Now in my ‘Summary’ field I want to extract some data for the JSON, but for some reason I don’t get what I expect from my example extraction.

After a lot of trial and error, i think the JSON is not being read as a string before being parsed to an. object.

To test this, I replaced the token with the equivalent string taken from the first screenshot, but not before replacing all the quotation marks manually.

I get the desired result.

I will take a look at Clause etc, but for now, but I don’t understand is why is my JSON not being read as a string before being parsed to an object and why changing all the quotation marks affects the outcome.

Regarding the quotation marks: straight (or neutral) quotation marks from the keyboard work, but not curly (or topographic) ones.

Thank you for the additional information. I ran a few things through Claude, and I believe the answer is that Tape’s calculation field doesn’t let you get away with anything. If you try to explicitly define something and the code does not support it, you’ll get an error. That doesn’t mean that you won’t be able to save the field, although sometimes it does. I think with undefined errors, it lets you get away with it, but with actual missing code, it doesn’t.

This is why it works with your raw JSON but not with @JSON because the code has everything it needs to do the job, whereas with @JSON, there’s no preview (which is the REAL problem here, grrr) so the system cannot use an item to determine what’s going on.

I want to show you a couple of examples, this is all test data anyways.

This is my JSON that’s being collected over four different apps into my “Projects” app: Test JSON - Pastebin.com

Here is my table summary, which converts JSON to HTML (apologies for all the extra spacing): Sample HTML - Pastebin.com

As you can see in my table summary HTML, just shortly down from the top, even though my JSON array contains de, tsk, att and item, I am EXPLICITLY telling the code that they are there and how to handle them if it can’t find them. Whereas your code is not doing this.

I tried to get the first ID from the ‘de’ array just like you and got the same error. So I fed the issue into Claude and determined that it’s Tape’s problem. So this would work:

var raw = ï»ż@JSONï»ż;
html = "";

var json = {};

try {
  json = JSON.parse(raw);

  // Defensive access
  if (json && json.de && json.de.length > 0 && json.de[0].id) {
    html += json.de[0].id;
  } else {
    html += "Data structure not as expected";
  }
} catch (e) {
  html += "Error: " + e.message;
}

The code just has to be more explicit.

I just wanted to say also that I LOVEEEEE the fact that Tape calcs support HTML because it means that once you nail the JSON, you can get some absolutely beautiful interfaces inline with the rest of your fields. The above HTML/JSON combination gave me this and I think it looks awesome. It even folds checklist data into the summary as well.

3 Likes