I closed my transcription business a month ago but still had some outstanding invoices to collect, and they all lived in Freshbooks. However, I canceled my Freshbooks subscription -- prepaid through mid-July 2023 -- and so needed to download all the info, at least about the outstanding invoices1.
I figured, though, that I may as well download the entire archive of clients and invoices -- 10+ years' worth! -- for future marketing purposes, and just to have a nice record of the business.
Luckily, I had published this Python library for interacting with the Freshbooks API, which got me most of the way towards downloading, serializing and storing all the client and invoice data.
> mkdir all_my_invoices && cd $_
> virtualenv env && source env/bin/activate
> pip install ipython simplejson avt-fresh
import avt_fresh
import json as j
import pathlib as p
cl = avt_fresh.ApiClient(client_secret="...", client_id="...", redirect_uri="...", account_id="...")
clients = cl.get_all_clients()
client_ids = [c.client_id for c in clients]
clients_json = []
for client in clients:
d = c._asdict()
del d['email_contact_id_lookup']
del d['contact_id_email_lookup']
clients_json.append(d)
p.Path('all_my_clients.json').write_text(j.dumps(clients_json, indent=2))
import avt_fresh
import simplejson as json
import pathlib as p
cl = avt_fresh.ApiClient(client_secret="...", client_id="...", redirect_uri="...", account_id="...")
def get_and_serialize_all_invoices_for_client(client_id):
invs = cl.get_all_invoices_for_client_id(client_id)
serialized = []
for i in invs:
d = i._asdict()
for key, val in d.items():
if isinstance(val, dt.date):
d[key] = val.toordinal()
# some data
del d['allowed_gateways']
del d['line_description_line_id_dict']
del d['line_id_line_dict']
new_lines = []
for l in d['lines']:
dl = l._asdict()
for key, val in dl.items():
if isinstance(val, dt.date):
dl[key] = val.toordinal()
new_lines.append(dl)
d['lines'] = new_lines
serialized.append(d)
return serialized
clients = json.loads(p.Path('all_my_clients.json').read_text())
invoices = {}
for client_id in client_ids:
invoices[client_id] = get_and_serialize_all_invoices_for_client(client_id)
p.Path('all_client_invoices.json').write_text(json.dumps(invoices, indent=2))
I'm not sure how likely I am to circle back and improve this library since I don't expect to use Freshbooks again in the future. However, there are a few low-hanging fruits which would make it a little nicer to work with, especially when it comes to serialization:
dataclasses instead of NamedTuplesserialize method which handles the date and Decimal objectsI should mention that Freshbooks is the only SaaS I've run into that will immediately shut your account down when you cancel your account, even if you've already paid for a year's subscription! I had to beg them to give me a brief window of access in order to perform the above steps -- five days to be exact, even though I still had 2+ weeks left in the prepaid subscription.↩