Import/remap CSV

Overview

This is the script I use to manually import my Canadian transactions.

You install it the same way you install Simple CSV Import, by copying the Code.js and Index.html files into a new Apps Script project.

You choose a prepared CSV file from your computer and it shows a preview of the number of new vs. duplicate transactions and which headers match before you continue.

Why?

I wrote it because the existing tools all address preparing CSV files: remapping columns to match the Transactions sheet, etc.

… but in my case preparing the files was already well addressed by existing tools like Excel and scripts — but importing the prepared files remained fraught. I found copying and pasting and detecting duplicate transactions error prone for example.

Preparing data for import can in general be a broad topic … I wanted something that solved the orthogonal problem of importing it once prepared.

Like Import CSV Line Items Basic Bank CSV, this script:

  • Maps columns to the Transactions sheet by (case-sensitive) header name.
  • Derives the month and week columns from the date, and the description from the full description.

… but unlike it, this script:

I’m grateful for any feedback especially if you also find this useful!

Next steps

Improve the UI, maybe as a sidebar like Import CSV Line Items vs. a dialog like it is now?

Wow @jack.bates, this looks amazing! Thank you for sharing your solution!

-Alice
Tiller Evangelist

Bluesky, Instagram, Facebook, LinkedIn

1 Like

I updated this script to support Amazon, Apple Card, Mint, PayPal, Venmo, YNAB, etc., like Import CSV Line Items and the CSV Spreadsheet Importer Multitool do … What do you think?

I differentiated the script from the existing tools by not remapping/transforming files, but then I got inspired by @randy’s Simple CSV Import and CSV Spreadsheet Importer Multitool :heart:. Data transformation is a broad topic but spreadsheet formulas probably solve a lot of it. And if they don’t or you prefer your own external workflow, the script still enables that, as before.

The mappings/transformations (from Amazon, etc.) are read from spreadsheet formulas, like Simple CSV Import and the Multitool do.

The script detects the format in the file:

It shows a preview:

and this is the feedback you get if you manually select an incompatible format:

Install

The same as before, copy the Code.js and Index.html files into a new Apps Script project, just like Simple CSV Import. But to remap/transform Amazon, etc. third-party files it now depends on HyperFormula, so you need to copy hyperformula.full.min.js to the Apps Script project as well. Find that file on e.g. jsDelivr.

HyperFormula is optional — without it the script works the same as it did before.

Finally, to remap/transform Amazon, etc. files you need a sheet named Crosswalks, mapping those formats to your Transactions sheet. Here’s an example of that sheet and here are the same formulas in a Markdown table:

Date Description Merchant Name Category Hint Category Tags Amount Account Account # Institution Transaction ID Check Number Full Description Note Metadata
Amazon Line Items '=OrderDate '="[Amazon Item] " & Title '=Category '=-ItemTotal '=PaymentInstrumentType Amazon '=“amazon:” & OrderID '="Amazon Order ID " & OrderID & “: " & Title & " (” & ASINISBN & “)” '=CarrierNameTrackingNumber '=“{"amazon":{"account":"” & OrderingCustomerEmail & “","category-hint":"” & Category & “","id":"” & OrderID & “","isbn":"” & ASINISBN & “","item-price":"” & PurchasePrice & “","payment-type":"” & PaymentInstrumentType & “","quantity":"” & Quantity & “","seller":"” & Seller & “","shipDate":"” & ShipmentDate & “","total":"” & ItemTotal & “","tracking":"” & CarrierNameTrackingNumber & “","type":"purchase","unspsc":"” & UNSPSCCode & “"}}”
Amazon Returns '=RefundDate '=“[Amazon Refund Item]” & Title '=Category '=RefundAmount + RefundTaxAmount Amazon '=“amazon:” & OrderID '="Amazon Order ID " & OrderID & “: " & Title & " (” & ASINISBN & “)” '=“{"amazon-refund":{"category-hint":"” & Category & “","id":"” & OrderID & “","isbn":"” & ASINISBN & “","order date":"” & OrderDate & “","quantity":"” & Quantity & “","refund reason":"” & RefundReason & “","seller":"” & Seller & “","type":"refund"}}”
Amazon Business Line Items '=OrderDate '="[Amazon Item] " & Title '=ProductCategory '=-ItemNetTotal '=PaymentInstrumentType Amazon '=“amazon:” & OrderID '="Amazon Order ID " & OrderID & “: " & Title & " (” & ASIN & “)” '=CarrierName & “(” & CarrierTracking & “)” '=“{"amazon-business":{"account user email":"” & AccountUserEmail & “","account-group":"” & AccountGroup & “","account-user":"” & AccountUser & “","asin":"” & ASIN & “","brand":"” & Brand & “","category":"” & ProductCategory & “","discount":"” & PricingDiscountAppliedOff & “","discount-program":"” & DiscountProgram & “","id":"” & OrderID & “","item-price":"” & PurchasePPU & “","order-tax":"” & OrderTax & “","part-number":"” & PartNumber & “","po-number":"” & PONumber & “","quantity":"” & ItemQuantity & “","seller":"” & SellerName & “","shipment-date":"” & ShipmentDate & “","shipping-address":"” & ShippingAddress & “","tracking":"” & CarrierTracking & “","unspsc":"” & UNSPSC & “"}}”
Apple Card '=TransactionDate '=Description '=Merchant '=Category '=-AmountUSD Apple Card Apple Card '=“{"appleCard":{"category":"” & Category & “","clearing date":"” & TEXT(ClearingDate,“yyyy-MM-dd”) & “","type":"” & Type & “"}}”
Mint '=Date '=Description '=Category '=IF(TransactionType = “debit”,-Amount,Amount) '=AccountName '=OriginalDescription '=Notes
PayPal '=Date '="[PayPal] " & Type & " / " & Name & " / " & FromEmailAddress & " → " & ToEmailAddress & " / " & ItemTitle '=Net PayPal PayPal '=“paypal:” & TransactionID '=Note '=“{"paypal":{"balance":"” & Balance & “","currency":"” & Currency & “","fee":"” & Fee & “","from":"” & FromEmailAddress & “","id":"” & TransactionID & “","itemid":"” & ItemID & “","name":"” & Name & “","subject":"” & Subject & “","title":"” & ItemTitle & “","to":"” & ToEmailAddress & “","type":"” & Type & “"}}”
Venmo '=Datetime '="[Venmo] " & Type & " / " & From & " → " & To '=AmountTotal Venmo Venmo '=“venmo:” & ID '=Note '=“{"venmo":{"destination":"” & Destination & “","fee":"” & AmountFee & “","from":"” & From & “","id":"” & ID & “","note":"” & Note & “","source":"” & FundingSource & “","status":"” & Status & “","title":"” & To & “","type":"” & Type & “"}}”
YNAB '=Date '=Payee '=Category '=Inflow - Outflow '=Account '=Memo '=“{"ynab":{"category group":"” & CategoryGroup & “","cleared":"” & Cleared & “","flag":"” & Flag & “","memo":"” & Memo & “"}}”

I copied the formulas from the CSV Spreadsheet Importer Multitool master, which replicates Import CSV Line Items.

Why?

  • Like Import CSV Line Items the script batch-imports Amazon, etc. CSV files in one shot vs. the Multitool which prepares data for copying and pasting. But unlike this script the Multitool is interactive — you can preview the data and update the formulas “live”.
  • Duplicate detection. Some Import CSV Line Items formats offer it and some don’t, whereas this script always does, either via a transaction ID column or a derived content ID. All formats, existing ones or ones you add yourself, have it.
  • Customization. Add formats yourself or customize existing ones (like Simple CSV Import and the Multitool allow you to do). Customize (and share!) automatic workflows.
  • The script doesn’t create the “offset transactions” that Import CSV Line Items does, however …

The Crosswalks sheet is mapped to your Transactions sheet by (case-sensitive) header names, and the Amazon, etc. files are mapped to the Crosswalks sheet via named expressions.

Expression names are case insensitive and limited to letters and numbers (approximately) so the script rewrites e.g. “Carrier Name & Tracking Number” to “CarrierNameTrackingNumber”. It ignores more specific naming rules for now …

Example

See the Crosswalks example for context but,

'=AmountTotal Map a CSV column (e.g. “Amount Total”) to your Transactions sheet.
'=-ItemTotal Arithmetic, e.g. change the sign of the “Item Total” column.
'=Inflow - Outflow Combine two or more columns.
'=IF(TransactionType = "debit",-Amount,Amount) Functions, e.g. the sign of the “Amount” column depends on its “Transaction Type”.

I’m grateful for any feedback!

2 Likes

Thank you @jack.bates for making this. Took a look at your script, love how uncomplicated it is. I think back to my early things I coded and it’s literally laughable!

I didn’t try it since I don’t have a CSV on hand to test it, but it looks great!

-Alice
Tiller Evangelist

Bluesky, Instagram, Facebook, LinkedIn

1 Like

@TillerAlice Thanks a lot for reviewing the code and your encouragement I really appreciate it!

1 Like

You might appreciate that I’m at the Google I/O conference this week. Learned yesterday how easy it is to use AI Studio to generate an API key for Gemini and that you can generate Google Apps Script right from there!

-Alice
Tiller Evangelist

Bluesky, Instagram, Facebook, LinkedIn

1 Like

Super cool thanks for thinking of me and sharing! I’m interested in Apps Script tips like this so please keep 'em coming and have fun at Google I/O!

@jack.bates I had SO MUCH FUN at I/O.

Since you asked… I have a Apps Script tip that came up this week.

When I want to save a property… for example, in my personal Add-on I allow the user to choose which Google Gemini model they want to utilize from a drop down menu.

To save the model I need to call PropertiesService.

If I use
const userProperties = PropertiesService.getUserProperties();

Then the value is saved across spreadsheet files. I pretty much never want that. So INSTEAD use
const userProperties = PropertiesService.getDocumentProperties();

getDocumentProperties() isolates the saved value to that particular spreadsheet (assuming you’re coding SpreadsheetApp).

Hope this was a helpful tip!

-Alice
Tiller Evangelist

Bluesky, Instagram, Facebook, LinkedIn

1 Like

Nice thanks for this tip! I’m curious about your personal add-on is it Tiller/finance related? Have you posted about it?

Hi @jack.bates, you can see my personal add-ons at http://alicekeeler.com/codedbyalice. They are pretty much all designed to support teachers, but I would love to hear what you think about them. It’s a hobby.

-Alice
Tiller Evangelist

Bluesky, Instagram, Facebook, LinkedIn

2 Likes

Oh WOW! This is super inspiring, especially the way you visually document them, I’m taking so many notes!

I’ve been wondering how best to distribute this script: by copying and pasting the files like Simple CSV Import or via the marketplace, like your add-ons … Copying and pasting has the advantage of keeping things simple, and keeping you in control (you can verify and edit the code directly) but I wonder if copying and pasting code is too high a barrier for many? From your experience with the marketplace, do you have any tips for me?

1 Like

What do you think I should rename this script to?

When I wrote it I just wanted to solve this feature request: to import more than the six columns that Basic Bank CSV allows. So I called it “Import Prepared CSV” to differentiate it from the other CSV tools (Import CSV Line Items, Simple CSV Import and CSV Spreadsheet Importer Multitool). But now that it supports “unprepared” as well as “prepared” CSV files, including custom formats that you add yourself, the name doesn’t fit …

“Import/remap CSV”?