Booking Amazon Purchases

How can one automate the booking of Amazon purchases? Typically, a payment source is involved (credit card, gift card, etc.). And then, there is the list of purchases that can be downloaded from Amazon. How can these be connected and booked correctly?

Connecting these seems to imply that one must import both sides (credit card transactions, and order history) together, and link them with one another (a database “join” operation) during the import process. This is a heavyweight approach which can stall your import process if something goes wrong. More importantly, it becomes messy if one side is more recent than the other, which is almost always the case. And finally, it can get a bit messy if you frequently use multiple payment sources (eg: gift card + credit card) on orders, which is a common experience.

Here is an alternative approach that has none of these issues and has dependably worked for me for years. It involves using Zerosum accounts:

Approach

  1. Credit card transactions are booked to a Zerosum account:
    2022-09-02 * "AMZN Mktp US*1FASU238B"
      Liabilities:Credit-Cards:MyCard  -18.92 USD
      Assets:Zero-Sum-Accounts:Amazon:Purchases
    
  2. Amazon purchases are booked from the Zerosum account to the expense account:

    2022-09-03 * "Cool Stealth Canoe, Black"
      card: "Gift Certificate/Card and Visa - 1234"
      Assets:Zero-Sum-Accounts:Amazon:Purchases  -18.92 USD
      Expenses:Outdoor-Activities:Equipment
    
  3. In most cases, the transactions in the Zerosum account will sum to zero, and you are all done! Optionally, if you use the zero sum plugin, it will match transactions above in memory, that can help in case you have unmatched, left over transactions.

Approach - Returns

The same approach works well for returns too. The same three steps above look like:

  1. The original Amazon order (from the Amazon importer):

     2022-09-03 * "Soap"
       card: "Gift Certificate/Card and Visa - 1234"
       Assets:Zero-Sum-Accounts:Amazon:Purchases   -20.02 USD
       Assets:Zero-Sum-Accounts:Stuff-Returned-To-Retailer
    
  2. Credit card transaction for the return (from the credit card importer):

    Order transactions are debits and booked to the :Purchases Zerosum account. Return transactions are credits and booked to the :Returns Zerosum account.

     2022-09-02 * "AMZN Mktp US*1FASU238B"
       Liabilities:Credit-Cards:MyCard             20.02 USD
       Assets:Zero-Sum-Accounts:Amazon:Returns
    
  3. Return transaction (from the Amazon importer):

     2022-09-10 * "Return for Order 111-3961666-5760248"
       Assets:Zero-Sum-Accounts:Amazon:Returns    20.02 USD
       Assets:Zero-Sum-Accounts:Stuff-Returned-To-Retailer
    

Pros and Cons

  • With this approach, there is no dependency on the credit card used to include metadata that maps to the Amazon order id. I could even pay with an Amazon gift card (even partially), and everything works out of the box.
  • The approach decouples the payment with the classification of transactions. This way, you are not forced to import and classify purchases each time you download your credit card transactions (which is fully automated for me, and takes like 5 seconds, which means I tend to do it frequently). Your pending amazon purchase details are neatly booked into the Assets account as a negative balance (or vice versa), and thus also [[ reflect ]] your state correctly.
  • Of course, the decoupling means you can’t see the expense account on the payment transaction directly. If you feel a need for this, it’s simple to solve using zerosum’s linking feature that uses Beancount links to connect the two transactions. Fava will then display this link as well

Implementation

  • Order data
    • I download order data from Amazon using their GDPR request
    • I use my Amazon GDPR importer to import these transactions
    • You can also use Beancount’s built in csv importer, which suffices, along with smart_importer, both seen in the config below:
      from smart_importer import apply_hooks, PredictPayees, PredictPostings
      from beancount.ingest.importers import csv
      Col = csv.Col
      CONFIG = [
          # GDPR Download
          # limitations: doesn't include coupons and discounts
          apply_hooks(csv.Importer({Col.DATE: 'Order Date',
                        Col.NARRATION1: 'Product Name',
                        Col.AMOUNT: 'Total Owed',
                        Col.LAST4: 'Payment Instrument Type',
                        },
                        'Assets:Zero-Sum-Accounts:Amazon:Purchases',
                        'USD',
      
                        ("Website", "Order ID", "Order Date", "Purchase Order Number", "Currency", "Unit Price",  "Unit Price Tax",
                         "Shipping Charge", "Total Discounts", "Total Owed", "Shipment Item Subtotal", "Shipment Item Subtotal Tax",
                         "ASIN", "Product Condition", "Quantity", "Payment Instrument Type", "Order Status", "Shipment Status",
                         "Ship Date", "Shipping Option", "Shipping Address", "Billing Address", "Carrier Name & Tracking Number",
                         "Product Name", "Gift Message", "Gift Sender Name", "Gift Recipient Contact Details"),
      
                        encoding='utf-8-sig',
                        institution='Amazon',
                        invert_sign=True),
             [PredictPostings()]),
      ]
          
      

      As usual, put the above into my.import and run bean-extract my.import ~/Downloads to import your Amazon purchases.

  • I use smart_importer to auto categorize both credit card transactions and Amazon GDPR transactions above
  • Alternatively, see this post to the Beancount mailing list showing how ChatGPT can classify your expenses.

  • Gift card data
    • I screenscrape (copy-paste) my Amazon gift card transactions from the webpage as a tab-separated file from here, and import this as a payment method. At the end of this, I have a gift card balance that usually agrees with what my Amazon account shows.
    • The importer for the screenscaped gift card data is simple and is here.

Amazon used to provide a “Category” column (eg: Kitchen, Sporting, etc.) that worked together with smart_importer to book almost any, never-seen transaction correctly. They don’t any more. Despite this, most transactions still get categorized correctly by smart_importer

Conclusion

  • The above steps are almost completely automated using my Amazon GDPR importer Of course, categorizing what smart_importer missed might take a couple minutes
  • You still need to download the GDPR zip file manually, which involves an email confirmation and then waiting for a link, but is a highly reliable process
  • I personally download Amazon data only once in several months, though if you need to stay on top of your expenses, doing it more frequently is not a problem
  • The Zerosum accounts typically work out to zero right away after the import, and you are all done. Occasionally they don’t, and it’s usually very easy to find the offending transaction and fix it
  • It doesn’t matter how frequently you choose to import from the various data sources: credit cards, banks, Amazon gift cards, and Amazon purchase and return transactions. The above approach simply works. Transactions not imported on one side yet simply get stashed into your Zerosum account, patiently waiting to be reconciled whenever you import the other side

Notes mentioning this note