Use Git and docs-as-code to track and manage freelance/consulting projects, clients, and associated payments and invoicing.
Warning
|
This is a minimum-viable proof of concept (MVP). It is mainly intended to be exemplary and to poll for interest in further development. |
Here is what the Clientele-as-Code project can already enable:
-
Store client metadata and payment/invoice records in a YAML file.
-
Generate invoices from the YAML sources:
-
HTML and PDF output formats
-
AsciiDoc source files written automatically
-
-
Track payments and unpaid invoices.
See the demo PDF artifacts:
There are a few benefits of this approach that I have considered, though they are yet to be demonstrated in practice to be true advantages.
- source control
-
Track all of your payments in Git.
- cost control
-
It’s free! Forever.
- total control!
-
You can customize and extend to your heart’s content.
- GAI/GPT assist
-
Use AI to help you fill out your invoices.
Cloud and other premium software that helps you track work and perform invoicing can get very expensive.
Filling out forms is less fun than letting a tool like GitHub Co-pilot help you write your invoices.
Free backup to GitHub, GitLab, or your private Git repo host of choice — or just backup to your backup. This is more confidence, privacy, and security than most paid systems provide.
While no real experience with the underlying technologies is required, this project will probably be most useful to people who already use Git and markup.
If you want to heavily customize the invoice template, you will need to use Liquid, which is designed for non-programmers. This is also the only case in which you would need to know any AsciiDoc, and to be frank, AsciiDoc with Liquid markup mixed in is not the easiest way to learn either format if you are not already adept at using markup.
The YAML files as currently implemented should be fairly intuitive for “less-technical” users. While they can be “broken,” so to speak, this whole system is safe for novice users — it does not automatically send anything to clients. If something is broken, you’ll know it before your clients see anything.
It’s not yet clear if Clientele-as-Code will make invoicing more collaborative for large teams, but my hope is that it will offer all the advantages that source-control systems have provided for collectively managing product codebases. I can imagine a situation where multiple workers at a firm can track their time on a given project, and an additional worker can handle the actual generating and management of invoices, all from a central repository.
This is a quickstart guide to getting up and running with Clientele-as-Code. More thorough documentation will be a feature of further development, if this MVP is successful.
-
Docker OR a proper Ruby environment (2.6+).
It is strongly recommended to use the provided Dockerfile to generate a Docker image that will run the script in a temporary container. This process is very fast.
Docker requires a proper Unix-like environment, so either MacOS or Windows with WSL2 will work, in addition to native Linux.
If you are already a Ruby user, try bundle install
and bundle exec ruby invoices.rb <client-name>
after cloning the repo.
-
Navigate to your main (global) work projects directory in your terminal application.
Examplecd ~/Documents/work/projects
-
Get this repository using either of these methods:
-
Clone the repo with Git.
git clone https://github.com/DocOps/clientele-as-code.git invoices
-
Download the ZIP file and extract it.
curl -L https://github.com/DocOps/clientele-as-code/archive/refs/heads/main.zip -o clientele-as-code.zip && unzip clientele-as-code.zip && mv clientele-as-code-main invoices && rm clientele-as-code.zip
-
-
Change to the project directory.
cd invoices
-
Build the Docker image.
docker build -t clientele:latest .
Test the procedure using the sample data provided.
-
Run the script to generate the latest invoice.
./invoice.sh acme-corporation
This generates the latest invoice to the path
clients/acme-corporation/generated/
in AsciiDoc (.adoc
), HTML5 (.html
) and PDF (.pdf
) formats.NoteIf this step does not work, you may need to run chmod +x invoice.sh
to make the script executable. -
Try it with a specific (previous) invoice ID.
./invoice.sh acme-corporation 001 ./invoice.sh acme-corporation 002
Note, there are three sample invoice periods, each showing off different aspects of the rendering system.
Invoice 001
received multiple payments but was paid off.
You’ll need your own versions of the global _config.yml
file, which reflects your company details and configuration settings.
To establish this, edit the _config.yml
file in the root directory.
Note
|
If you already have a _config.yml file in your root directory, you may name the file _invoices.yml , or just add the provider and settings blocks from our _config.yml to your own, assuming they do not conflict.
|
Then make a copy of the clients/acme-corporation/
directory and modify it to meet your first client’s specifics.
-
If necessary, make a client directory.
mkdir clients/your-client-id
-
Copy the data files from the example directory.
cp -r clients/acme-corporation clients/your-client-id
Alternatively, you an just rename the directory and edit the files in place, if you don’t care to keep a copy of the example data.
With your source files customized, you can run the script to build invoices for your client.
./invoice.sh your-client-id
If you are not happy with the invoice output, simply make changes to your data files and try this command again.
Some basic configuration of how the invoice will render can be established in the settings
block inside the _config.yml
file.
These are presented as commented-out lines that express the default value for each.
Uncomment and modify them as needed.
Further customization can be done in the templates/invoice.asciidoc
file.
Here are some tips for using this platform:
-
If you are brand new to YAML, check out this introductory article or this intro video.
-
Invoice records should be listed most recent to oldest — or at least the current/latest should be the first in the sequence, so it can be generated without specifying an ID in the
./invoice.sh
command. -
Adding a
paid
property to an invoice’sdates
block will trigger the-PAID
filename tag and the PAID stamp on the PDF version of the invoice. Registeringpayments
will not mark an invoice paid, even if the total is met.dates: sent: 2024-09-01 due: 2024-09-30 paid: 2024-09-15 # whenever this appears, the invoice is considered fully paid
If you already have a directory containing client directories, you can integrate this project into that structure.
- Clone to local
-
If your existing directory is not already a Git repository, you can clone the repository directly into it.
git clone https://github.com/DocOps/clientele-as-code.git .
- Download and extract to local
-
If you just want to add these files to an existing repository, be sure changes are committed and/or the path is backed up:
curl -L https://github.com/DocOps/clientele-as-code/archive/refs/heads/main.zip -o clientele-as-code.zip && unzip clientele-as-code.zip && mv clientele-as-code-main/* . && rm -rf clientele-as-code-main clientele-as-code.zip README.adoc spec
Integration into an existing codebase/repo should be made far more elegant if this project is released as a proper Ruby gem (see Development).
As mentioned, this is simply a proof-of-concept, mainly:
-
To demonstrate the various ways my preferred AYL DocStack (AsciiDoc, YAML, and Liquid) can be used to solve diverse documentation problems with a code-like, Git-friendly approach.
-
To see if it makes sense even for me to use on a regular basis to track my own clients and their payments.
-
Hopefully, to demonstrate how Docker can make docs-as-code projects more accessible.
If you are interested in this project, give it a star and maybe post an Issue requesting a feature or fix that you need.
Here are the big changes I expect to make to this project if others really want to take advantage of it:
- more customizaton
-
The output can be endlessly customized, especially for international users. We should make this as convenient as possible by adding it to the
_config.yml
file. - more features
-
-
VAT handling for European users.
-
Dynamically customize invoice filenames.
-
- contracts-as-code
-
Single-sourced, markup-formatted freelance/consulting/etc contracts that can be managed in Git and digitally signed with extraordinary ease. See Codewriting Contracts.
- proper gem release
-
Package and release the underlying code as a Ruby gem with a proper commandline interface (CLI).
- modularize template
-
The
invoice.asciidoc
template should be broken down into numerous “partials” that are included into the main template, so users can customize any one part of it without having to maintain a fork of the entire file. - improve invoice theming
-
Both the PDF and HTML output are basically Asciidoctor default, and could use some better styling.
- real documentation
-
There should be a proper reference for the configuration properties, for starters, and maybe a tutorial.
- move heavier logic to Ruby
-
Some of the parsing now performed in the Liquid template would be better handled in Ruby, meaning we would transform the
invoice
data object before handing it off to Liquid. - separate core source from demo content
-
Right now if you clone or fork this repo, your code will diverge as soon as you customize the
_config.yml
file or remove theacme-corporation
directory. A proper release will separate those files while making quickstart demos still possible. - better practices
-
-
The
Gemfile.lock
file should be tracked rather than Git-ignored. -
Gems should persist in a Docker named volume
-
Unit tests should be added to Ruby script.
-