accotron devlog for 2023W06
2023-02-05 by Evrim Ă–ztamur

accotron devlog for 2023W06

Journal timelines

I had referred to the beancount timeline representations used by some sections in their wiki before, but I didn’t get around to coming up with a good design that meshes well with the rest of accotron. Having settled on an alternative that is a bit more helpful, I quickly drafted a short post on that. Here’s a teaser:

Screenshot of the journal timeline widget

Annotation caching

Generating the annotations is fast enough that I think it will be included as a post-processing step after loading all journals (during organisation initialisation) and whenever more journals are added. On the other hand, generating them on the fly makes them difficult to refer to these annotations and their contents (like added/reversed lines) when accessing them from other reports. These reports will list tens or hundreds of journals at once, and caching the annotations is vital.

Having noted how I was planning on approaching journal annotation caching, I ended up finding a decent but not entirely satisfactory solution. Despite the annotation generation being fast enough, I would like to use the data they provide in other reports to perk them up a bit better.

I ended up using only the json_serializer option. The reason for this is that the deserialization step converts to the implicit type of the database field. For a XeroJournalEntry (XJE) database-equivalent object, there’s the application counterpart XeroJournal (XJ). While the XJE is a 1:1 mapping of the database into SQLAlchemy, XJ is the object that the rest of the application interacts with. Such pairs exist for essentially all of the Xero data types.

In 2022W48, I mentioned a potential merge. I ultimately settled to not doing this, because merging the pair into one class meant that a lot of application logic would spill into the storage module.

Reaping the benefits of that, I ended up not making any adjustments to the storage module, but overriding the XeroJournal object with the following method. Verbatim, the code looks like the following:

annotation: XeroJournalAnnotation | None = None

@property
def annotation(self):
    return self._annotation

@annotation.setter
def annotation(self, val):
    if isinstance(val, dict):
        self._annotation = XeroJournalAnnotation.parse(val)
    elif not (val is None or isinstance(val, XeroJournalAnnotation)):
        raise ValueError(
            f"value provided for {self.__class__.__name__}.annotation is invalid"
        )
    else:
        self._annotation = val

Weird approach having to write to _annotation, but it ended up being the most straightforward. The code itself can be cleaned up and moved into some form of validation module (or I can just go ahead and use a real validation module, like pydantic, but it doesn’t seem necessary now).

The problem is that XeroJournal is a dataclass (essentially an automatic constructor generator in Python-land), and dataclasses do not have a good way to add a ‘setter’ to their fields directly.

It works reliably, and until I came to this exact solution, I found myself visiting the topic of caching/JSON deserialization over the last month and running in circles—it ended up being a lot simpler than I thought.

The XeroJournalAnnotation.parse(val) part is important to point out. Earlier versions, I was using XeroJournalAnnotation(**val) to just roll the JSON into the dataclass. The problem is that the AnnotationCategory enumerator does not automatically get converted from int, so there has to be a parse function in place.

Sales tax comparison report (STCR)

Following the move to a new grid system, the STCR broke down into an illegible mess.

« Work on fixing STCR layout »

Sales tax report

There’s the super-report of STCR, which is the sales tax report (STR) itself.

The STR is the view of VAT items for a given point in time. The SCTR works via generating two STRs, one at the ‘before’ and another at the ‘after’ date, and takes the difference between the two.

The layout of the STR was also broken, and will be fixed alongside the STCR. Ultimate goal is to make sure that these two reports are MVP-ready by the end of the week.

For the next week

I am planning on getting the MVPs ready for the account-level reports, that is, the income statement and balance sheet (plus the respective comparison reports).

My guess is that I will have to build some sort of table rendering macro system, as the manual templating work involved with these tables is getting a bit cumbersome.