Tracking how well my company is doing using Org mode
I keep almost everything related to my company and development work in Org mode . Whilst I do use online accounting software (which comes backed by actual accountants) I keep track of projected and actual invoicing amounts in a single Org mode file.
The file contains a heading for each month and then a table listing each project and estimated and actual invoicing totals.
Here is a snippet of how this looks (with made up clients and totals, and no actual figures as I haven't completed invoicing yet for November):
** November 2019
| Client | Project | Estimated | Actual |
|-----------+------------------------------------+-----------+--------|
| Umbrella | Monthly support | 350.00 | |
| Umbrella | Booking screen | 1953.00 | |
| Paw City | Add new payment card support | 830.00 | |
| Top Tours | Update based on platform bug fixes | 325.00 | |
| Zinc X | Specification for v2 changes | 1517.53 | |
| Zinc X | Implement new login UI | 260.00 | |
|-----------+------------------------------------+-----------+--------|
| | | 5235.53 | 0 |
#+TBLFM: @>$3=vsum(@2..@-1) :: @>$4=vsum(@2..@-1)
Org mode has fantastic support for creating tables, and can function as a mini-spreadsheet. At the bottom of the table is a formula to add up the estimated and actual columns. A quick C-c (for non Emacs users that is Control + C) updates the values in the table.
My online accounting software isn't bad but it's not that easy to get a quick overview of company performance by year, month and by client.
Since Org mode files are plain text you are open to the possibility of writing scripts to generate whatever data you need.
This morning I put together a quick Python script to parse my invoicing file and give me a quick summary by month and client for each year. I don't write Python very often these days, so any excuse is a welcome one :)
If you are interested here it is. Note this is quick and dirty and relies on a consistent format for my file. As a couple of my clients changed company names I have the ability to specific aliases so they get resolved to the same client when doing the summaries.
client_aliases = {
}
ordered_months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
class Invoice:
def __init__(self, month, year, client, project, estimated, actual):
self.month = month
self.year = year
if client in client_aliases:
self.client = client_aliases[client]
else:
self.client = client
self.project = project
self.estimated = estimated
self.actual = actual
def read_invoices(text):
current_month = None
current_year = None
invoices = []
for line in text.split("\n"):
line = line.strip()
if line.startswith("* ") and not line.startswith("* Invoicing"):
break
if line.startswith("** "):
current_month, current_year = line[3:].split()
elif line.startswith("|"):
components = [x.strip() for x in filter(lambda x: x != "", line.split("|"))]
if components[0] == "Client" or components[0].startswith('-') or components[0] == '':
continue
try:
estimated = float(components[2])
except ValueError:
estimated = 0
try:
actual = float(components[3])
except ValueError:
actual = estimated
invoices.append(Invoice(
current_month,
int(current_year),
components[0],
components[1],
estimated,
actual))
return invoices
def summary(invoices, year):
clients = {}
months = {}
total = 0
for invoice in invoices:
if invoice.year != year:
continue
months[invoice.month] = months.get(invoice.month, 0) + invoice.actual
clients[invoice.client] = clients.get(invoice.client, 0) + invoice.actual
total += invoice.actual
print(f"{year}\n--------")
print("By client:")
for client, client_total in clients.items():
print(f" {client}: {client_total}")
print("By month:")
for month in ordered_months:
print(f" {month}: {months.get(month, 0)}")
print(f"TOTAL: {total}\n")
invoices = read_invoices(open('aotm-finance.org').read())
summary(invoices, 2017)
summary(invoices, 2018)
summary(invoices, 2019)
Feel free to do whatever you want with the above code if you want to look to implement a similar system for yourself.
I'm sure I could do the same thing using an actual spreadsheet or countless other solutions, however the benefits of using plain text and having everything accessible in Org mode (and beorg on my iPhone/iPad) are overwhelming.