Skip to content

Latest commit

 

History

History
249 lines (188 loc) · 7.19 KB

README.org

File metadata and controls

249 lines (188 loc) · 7.19 KB

Welcome to EASY - Evented Accounting Sourced from Yaml

./easy_logo.png

Introduction

easy is a command line utility which generates artifacts typically used in accounting (but also other fields of business like invoicing) based on events provided as YAML files.

Overview

In principle easy is used to transform the business events given in YAML into other business artifacts like invoices (PDF documents) or journal files for use with ledger (a command line utility for plain text accounting.) Processing of events happens in 5 steps:

  1. Read input events
  2. Check input events against spec
  3. Augment input events
    1. Calculate derived values
    2. Resolve related events
  4. Check augmented event against spec
  5. Render event(s) through templates

Inputs

You can have as many YAML files as you want. Each YAML file contains one or more events. You are also free to categorize your YAML as you please. (E.g. We have one file for each customers invoices and a separate file for all settlements of the invcoices.)

All events are spec’ed in Clojure Specs. Some of the fields are mandatory, some are optional. See the specs for details. Here are some example Specs:

The easy code base is structured in a way that nested name spaces reflect the data structure that is used when writing events. (E.g. an event of type revenue has a field items, the spec for items will be in the name space revenue.item.)

Example

This is what a typical easy YAML document with one event might look like.

---
- type: revenue
  date: 2017-12-04
  customer: 6
  number: 2
  version: 1
  deadline: 30
  items:
    - rate: 200
      hours: 13.5
      beneficiary: phil
      discount: 0
      timesheet: path/to/timesheet.csv

Install

lumo via npm

Easy runs on lumo and uses node packages available via npm, as well as ClojureScript packages available via clojars (maven).

Hence you will need to install the npm dependencies via npm install.

clojars via Clojure’s CLI

ClojureScript dependencies tracked in deps.edn are installed via clj (aka. Clojure CLI). Please install clj as described here.

Debian packages

To render invcoice via latex you need to have at least the follwoing packages installed (Debian naming):

  • texlive-latex-base
  • texlive-latex-recommended

Usage

Easy provides two command line utilities: easy itself and collect. Here are some examples on how to use these.

You have to have some files in place in order to use easy. Please find the example directory of this repo and to run these examples.

Examples

Render ledger entries from events and run the through ledger to create a balance:

easy ledger -i events/invoices.yml | ledger bal -f -

Render invoice 18.2.1 to latex:

easy invoice -i events/invoices.yml -n 1.1.1

Sidenote: Easy invoice identifiers (invoice-no) are divided into three segments <customer-id>.<invoice-number>.<version>. In events thei can be given as invoice-no in aggregated form or separately as customer, number, and version.

Run a transform to see the transformed events, which is handy for writing templates:

easy transform -i events/invoices.yml

Or, just validate the input and exit:

easy validate -i events/invoices.yml

Easy alternatively takes events via STDIN:

cat events/invoices.yml | easy transform

This comes in particularly handy, when working with multiple event sources (i.e. YAML files)

cat events/*.yml | easy ledger | ledger -f - bal

(Please be ware that concatinating YAML files for easy only works if the files lack the YAML’s document prefix ---. The reason for this is explained in “On Frontmatter Templates”. And it doens’t really matter as you won’t be using cat anyways, just keep reading.)

Finally easy comes with its own utility to collect multiple event sources into a single event stream. Given a root directory for events it will find all files matching *.yml, load these, annotate each event with its origin (for locating issues quickly), and outputs a sorted stream of events.

collect events | easy ledger -y 2019

Yes, you guessed it, you can use -y <year> to filter events by year.

On Frontmatter Templates

A typical event source for easy might look like this:

- type: expense
  account: Aufwand:6940-Bankspesen
  payer: Joint
  amount: 5
  date: 2018-05-31
  description: Bankgebühren
- type: expense
  account: Aufwand:6940-Bankspesen
  payer: Joint
  amount: 5
  date: 2018-06-30
  description: Bankgebühren

Imaging the file going on like this. These are monthly occurring events with very high redundancy. In these cases you might want to use collect’s (and easy’s) capability to work with templates. This allows you to define common attributes of the list of events as a template using multiple YAML documents in one YAML file. That way you store the same information by writing:

---
type: expense
account: Aufwand:6940-Bankspesen
payer: Joint
description: Bankgebühren
---
- date: 2018-05-31
  amount: 5
- date: 2018-06-30
  amount: 5

Any list will be interpreted as a list of events, likewise any associative will be interpreted as template. Templates will completely replace any previous template.

Here is another example:

---
a: 1
b: 2
---
- c: 3
---
b: 4
d: 5
---
- e: 6
- f: 7

Will result in the following data structure:

- a: 1
  b: 2
  c: 3
- b: 4
  d: 5
  e: 6
- b: 4
  d: 5
  f: 7

Neat, isn’t it?

On Resolve & Context

This does only concern you if you are developing easy.

Easy uses a multimethod transform to transform events into a augmented form. Augmented meaning it has all the details calculated and associated data added to be used in a template.

The transformation of a single event might happen within a context. The context is the set of all events within a processing run. (Technically the context is not a set, but a map, where the values are lists of events and the keys are their corresponding type. This is for convenience because in almost all cases you only want to have events of a single type when using the context to resolve associated events. E.g. for a given invoice you want to resolve its settlement, or the other way round.)

Associated data is added via resolve-fns. The resolve-fns use the context to lookup other events. As the context contains only “mildy” and not fully transformed events the resolve-fn will very likely have to transform the resolved event in order to make it augmented. If this would mean that another resolve-fn is triggerd this will likely trigger an endless recursion of resolves.

To prevent this from happening you have to adhere to the following conventions: (A) Resolve functions that receive nil as the context should return the event untouched. (B) When calling transform from a resolve-fn, always pass nil as the context.