Skip to main content

I’ve got a nice little script within an automation to auto-categorize transactions in a bookkeeping base. It relies on a table for transactions with various fields, as well as a table called Rules, with the rules I want to create for my categorizations. The Transactions table has the following fields: 

*Name - single line text

Lowercase Name - formula

Matched Terms - single line text

*Vendor - single line text

Payment Type (tax purposes) - single line text

Payment Type - single select

Category - Linked field

Merchant - Linked field

Transaction Year - formula

 

The Rules table has the following fields: 

Match Term - single line text

Lowercase Match Term - formula

Payment Type - single select

Desired Vendor Name - single line text

Categories - linked field

Update Rule? - checkbox

 

It all works great, but now I want to add another parameter to the Rules table, a linked field to my table called Operational Expenses. The Transactions table already has a linked field called “Operational Expenses.” I want the script to see whenever I add a record to the linked field in Rules, that I want that same linked record added to the field in Transactions (same as with the Category item). However, I didn’t write this code, so I don’t know how to go about adding this. I’d also like for it to just do nothing if the Operational Expenses field is blank in the Rules table. Can anyone help me adjust the code so it can include this? Here it is below:

 

let inputConfig = input.config();
let transactionsTable = base.getTable("Transactions");
let matchTermsTable = base.getTable("📢 Rules");
let merchantsTable = base.getTable("🛒 Merchants & Tax Docs"); // Updated table name

// Get the record ID of the newly created transaction
let transactionRecordId = inputConfig.transactionRecordId;

// Fetch the transaction record
let transactionRecord = await transactionsTable.selectRecordAsync(transactionRecordId);

// Get the lowercase transaction name
let transactionName = transactionRecord.getCellValue("Lowercase Name");

// Fetch the transaction year
let transactionYear = transactionRecord.getCellValue("Transaction Year");

// Fetch all match terms
let matchTerms = await matchTermsTable.selectRecordsAsync();

// Check if there are any match terms
if (matchTerms.records.length === 0) {
console.warn("No match terms found in the Rules table. Exiting script.");
return; // Exit the script if no match terms are found
}

let matchedTerms = []; // Initialize matchedTerms here
let vendorToUpdate = ""; // Variable to store the vendor from the matched term
let paymentTypeToUpdate = ""; // Variable to store the payment type from the matched term
let categoryToUpdate = []; // Variable to store the categories from the matched term

// Loop through the match terms to check if the transaction name contains any of them
for (let matchTerm of matchTerms.records) {
let matchTermValue = matchTerm.getCellValue("Lowercase Match Term");
if (transactionName.includes(matchTermValue)) {
matchedTerms.push(matchTerm.name); // Store the matched term name
vendorToUpdate = matchTerm.getCellValue("Desired Vendor Name") || "Unknown Vendor"; // Default value
paymentTypeToUpdate = matchTerm.getCellValueAsString("Payment Type") || "Unknown Payment Type"; // Default value
categoryToUpdate = matchTerm.getCellValue("Categories") || []; // Default to empty array
break; // Exit the loop after the first match (if you only want the first match)
}
}

// Only update the transaction record if there are matched terms
if (matchedTerms.length > 0) {
// Construct the Merchant value
let merchantName = vendorToUpdate + " " + transactionYear;
console.log("Constructed Merchant Name:", merchantName); // Log the constructed merchant name

let existingMerchants = await merchantsTable.selectRecordsAsync(); // Updated table reference
let existingMerchant = existingMerchants.records.find(record => record.getCellValue("Name") === merchantName);

let merchantId; // Declare merchantId variable

if (existingMerchant) {
// If the merchant already exists, use its ID
merchantId = existingMerchant.id;
console.log("Found existing merchant:", existingMerchant.name); // Log the found merchant
} else {
// Create a new record in the Merchants & Tax Docs table if it doesn't exist
let newMerchantRecord = await merchantsTable.createRecordAsync({
"Name": merchantName // Assuming "Name" is the field in the Merchants & Tax Docs table where you want to store this value
});
merchantId = newMerchantRecord.id; // Get the ID of the newly created merchant record
console.log("Created new merchant:", merchantName); // Log the created merchant
}

// Update logic here...
let updateData = {
"Matched Terms": matchedTerms.join(", "), // Assuming this is a text field
"*Vendor": vendorToUpdate !== "Unknown Vendor" ? vendorToUpdate : null, // Only set if valid
"Payment Type (tax purposes)": paymentTypeToUpdate !== "Unknown Payment Type" ? paymentTypeToUpdate : null, // Only set if valid
"Category": Array.isArray(categoryToUpdate) && categoryToUpdate.length > 0 ? categoryToUpdate : null, // Only set if valid
"Merchant": merchantId ? [{id: merchantId}] : null // Only set if valid
};

// Log the update data to see what is being sent
console.log("Updating record with data:", updateData);

// Filter out any null values from updateData
updateData = Object.fromEntries(Object.entries(updateData).filter(([_, v]) => v != null));

if (Object.keys(updateData).length > 0) {
await transactionsTable.updateRecordAsync(transactionRecordId, updateData);
} else {
console.warn("No valid fields to update.");
}
} else {
console.log("No matched terms found. No updates will be made to the transaction record.");
}

Thank you so much!!

Hi,
here is more clean version of your script, auto-created
sorry, but I can’t understand what do you want to do.

check Rules table, linked field (‘Operational Expenses’?),
        record where ‘Category item’? is ….(something from script?)…
if value is not empty, update similar field in Transactions table,
      include ‘Operational Expenses’ into Update in script?


you can add any useful variable to the input config()
note that you can add not just record ID, but in many cases you can add record value(s)
i feel that more than 50% of your script can be performed by automation UI steps
but it’s just possible option
 

// === Config & setup ===
let { transactionRecordId } = input.config();
let tTable = base.getTable("Transactions");
let rTable = base.getTable("📢 Rules");
let mTable = base.getTable("🛒 Merchants & Tax Docs");

let tRecord = await tTable.selectRecordAsync(transactionRecordId);
let tName = tRecord.getCellValue("Lowercase Name");
let tYear = tRecord.getCellValue("Transaction Year");
let rules = await rTable.selectRecordsAsync();

// === Find match ===
let matched = rules.records.find(r => tName.includes(r.getCellValue("Lowercase Match Term")));
if (!matched) return console.log("No match found.");

let vendor = matched.getCellValue("Desired Vendor Name") || "Unknown Vendor";
let payType = matched.getCellValueAsString("Payment Type") || "Unknown Payment Type";
let cat = matched.getCellValue("Categories") || [];
let merchantName = `${vendor} ${tYear}`;

// === Find or create merchant ===
let existing = (await mTable.selectRecordsAsync()).records.find(r => r.getCellValue("Name") === merchantName);
let merchantId = existing ? existing.id : (await mTable.createRecordAsync({ Name: merchantName })).id;

// === Prepare & update transaction ===
let data = {
"Matched Terms": matched.name,
"*Vendor": vendor !== "Unknown Vendor" ? vendor : null,
"Payment Type (tax purposes)": payType !== "Unknown Payment Type" ? payType : null,
"Category": cat.length ? cat : null,
"Merchant": merchantId ? [{ id: merchantId }] : null
};

data = Object.fromEntries(Object.entries(data).filter(([_,v]) => v != null));
if (Object.keys(data).length) await tTable.updateRecordAsync(transactionRecordId, data);
else console.log("Nothing to update.");


 


Could you provide the following please?  Would make it a lot easier to help you!

  1. An example base with example data, with the automation set up
  2. Example output of the script with said example data

 


Hi all,

Thanks for the replies. I have an example base set up with this automation, I’ll share it via DM. The latest transaction is the example I’m using. 

This is what the Rules tab looks like: 


I went into the Rules tab and created a record (the last one at the bottom) that uses a piece of the transaction name as a Match Term (“ENTRY DESCR:FEE” in this example). This triggers the automation that looks up that match term in the Transactions tab (it technically looks up a lowercase version to make it all case-insensitive), and then updates the records it finds according to the Rules tab (updating the Vendor field with the Desired Vendor Name, the Payment Type field with the Payment Type specified in the Rules tab, the Category field with the Category specified in the Rules tab.

All of that is already working in my script. What I wanted to do is to add another linked field called Operational Expenses, and have the script update the field Operational Expenses for the record in question in the Transactions tab with whatever I specify in the Rules tab (in the example, the field would link to the record “Gusto” in the Operational Expenses tab). Basically, I want it to do the exact thing it’s already doing for the Category field, and if the Operational Expenses field in the Rules tab is blank, then to have the script do nothing. The resulting record in the Transactions tab would look like this: 


Does that make sense? Let me know if there are any more details I can add to help clarify. Since I didn’t write the code myself (and am not really versed in it), I don’t know how to duplicate the same mechanisms being used for the Category field matching to also apply to the Operational Expenses as well.