My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

Tracking how well my company is doing using Org mode

Matthew Kennard's photo
Matthew Kennard
·Nov 27, 2019

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.