Invoices
Generate invoices from a config file
Basic usage
Use the following to see how it works on the provided sample config (make sure to update the JIRA credentials with correct values).
lein run resources/config.edn
Options
The following options are available:
-n, --number Invoice number. In the case of multiple invoices, they will have subsequent numbers
-w, --when The date for which to generate the invoice
-c, --company The NIPs of companies for which to generate invoices. If not provided, all the companies will be used
-h, --help Display a help message
Config file
The config file should be a EDN file containing a list of invoices, seller info, optional font info and optional worklogs info, e.g.:
{:seller {(...)}
:invoices [(...)]
:font-path "/path/to/font"
:worklogs [(...)]}
:font-path
should be the path to a font file, e.g. "/usr/share/fonts/truetype/freefont/FreeSans.ttf"
See resources/config.edn
for an example configuration.
Seller
The Seller can have the following keys:
- :name - (required) the name of the seller, e.g. "Mr. Blobby"
- :address - (required) the address of the seller, e.g. "ul. Szeroka 12, 12-345, Buty"
- :nip - (required) the NIP of the seller, e.g. 1234567890
- :account - (optional) the number of the account to which the payment should go, e.g. "12 4321 8765 1000 0000 1222 3212"
- :bank - (optional) the name of the bank in which the account is held, e.g. "Piggy bank"
- :phone - (optional) the phone number of the seller 555333111
- :team - (optional) a team name, to be prepended to the name of the resulting pdf, e.g. "the A team"
Invoices
Each invoice can have the following keys:
- :buyer - the buyer's (i.e. the entity that will pay) information. This is required
- :items - a list of items to be paid for
- :imap - (optional) email credentials. These are needed if a confirmation email is to be sent
- :callbacks - (optional) a list of commands to be called with the resulting pdf file
- :seller - (optional) invoice specific overrides for the seller object
Buyer
The buyer can have the following keys
- :name - (required) the name of the seller, e.g. "Mr. Blobby"
- :address - (required) the address of the seller, e.g. "ul. Szeroka 12, 12-345, Buty"
- :nip - (required) the NIP of the seller, e.g. 1234567890
- :email - (optional) the email of the buyer, e.g. "faktury@bla.com". This is required if a confirmation email is to be sent
Items
The list of items should contain maps with a required :title
, optional :vat
(if not provided it is assumed that
item is VAT free), :to
(the date from which this item is valid), :from
(the date till which this item is valid),
:notes
(a list of extra notes to be added at the bottom of the invoice) and a key providing the cost of the item.
The price can be provided in one of the following ways:
- :netto - is a set price and will be displayed as provided
- :brutto - is a set price and will be first scaled down to netto
- :hourly - is an hourly price - worklogs will be queried in order to work out how many hours should be billed. If no worklog could be found (or its :worked is nil), this item will be skipped.
- :base + :per-day - in the case of a variable number of hours worked. :base provides the amount that would be paid
if
<number of hours worked> == <number of hours in the current month if full time> / per-day
. In the case of someone working full time, :per-day would be 8, and if the number of hours worked is the same as the number of working hours in the month, the final price would simply be :base. If someone worked part time, e.g. 4 hours daily, then :per-day would be 4, and if that person had worked exactly half the number of working hours in a given month, then the price will also be :base. Otherwise the final price will be scaled accordingly. This is pretty much equivalent to working out what the hourly rate should be in a given month and multiplying it by the number of hours worked in that month. If no:worked
value can be found (or if it's nil), this item will be skipped. - :function - an S-expression describing how to calculate the net value. Only numbers, basic mathematical operations (+, -, /, *) and timesheet specific variables are supported (:worked, :required). If a timesheet variable is used, but no such value can be found in the timesheet, an exception will be raised.
- :from - an ISO date specifying the date from which this item should be used in calculating invoices (any invoices generated for dates before this value will ignore this item)
- :to - an ISO date specifying the date up to which this item should be used in calculating invoices (any invoices generated for dates after this value will ignore this item)
If the price is to be calculated on the basis of a worklog, add a :worklog
key
and make sure the :worklogs
section has an item that can be used to access the worklog.
Examples:
; 8% VAT, and a price of 600, recurring every period before 2019-05-30
{:vat 8 :netto 600 :title "Shoes" :to "2019-05-30" :notes ["A note at the bottom"]}
; 12% VAT, and an hourly rate of 12, first appearing on 2019-07-01
{:vat 12 :hourly 12 :title "Something worth 12/h" :from "2019-07-01" :worklog "washed_dishes"}
; 23% VAT, working part time with a base salary of 5000
{:vat 23 :base 5000 :per-day 4 :title "Part time job at 5000" :worklog "cleaned_shoes"}]
; 23% VAT, with a custom function
{:vat 23 :function (* :worked (/ 10000 :required)) :title "Custom function"} :worklog :from-jira]
Worklogs
In the case of hourly rates or variable hours, the number of hours worked needs to be fetched
from a time tracker. Which requires appropriate credentials, described in the
following sections. Apart from provider specific values, each credentials map
must contain a :type
key that describes the provider, and a :ids
list, which
should contain all worklog ids that can be found in the given worklog. These ids
are used to link worklog values with items via the :worklog
key of items.
Simple lists
This is the basic worklog, i.e. a list of months with the amount worked provided (hours by default).
The unit can be changed via the unit
key and can be one of :hour
or :day
. Below is an example:
:worklogs [{:type :list
:ids [:cows-R-us]
:worklogs {"2020-09" {:count 12 :unit :day}
"2020-10" {:count 12 :unit :day}
"2020-11" {:count 54 :unit :hour}
"2020-12" {:count 5 :unit :day}
"2021-02" {:count 20 :unit :day}}}]
Jira
See Jira's and Tempo's documentation on how to get the appropriate tokens. Once the tokens are generated, add an appropriate worklog entry like the following:
:worklogs [{:type :jira
:ids [:from-jira]
:month-offset -2 ; Can be used to get a different month than the currently processed one. In this case, 2 months previous
:tempo-token "5zq7zF9LADefEGAs12eDDas3FDttiM"
:jira-token "qypaAsdFwASasEddDDddASdC"
:jira-user "mr.blobby@boots.rs"}]
Emails
Emails with worklogs should be sent in a psudo csv format, seperated by ;
or
whitespace. Use the :headers
key to describe what data is contained in each
column.
The emails are looked for in the :folder
folder of the email account, and all
emails from :from
(or anyone if :from
is nil or missing) and with the subject
contining :subject
formatted with the processed date.
Assuming the processed date is 2012-12-12, and the following configuration is provided:
:worklogs [{:type :imap
:ids [:item1 :item2]
:folder "inbox"
:host "imap.gmail.com"
:user "mr.blobby@boots.rs"
:pass "lksjdfklsjdflkjw"
:from "hr@boots.rs"
:subject "'Hours 'YYYY-MM"
:headers [:id :worked]}]
if hr@boots.rs
sends an email to the inbox
folder of mr.blobby@boots.rs
's
email account with the title Hours 2012-12
and the following contents (notice the underscores):
washed_dishes; 12
cleaned_shoes; 43
the following work logs will be found:
[{:id "washed_dished" :worked 12}
{:id "cleaned_shoes" :worked 43}]
Confirmation emails
Each invoice can also be sent via email to the appropriate seller. For this to work, the buyer must have an :email key set and a :smtp key with the :smtp settings for the email server should be provided.
:invoices [{:buyer {(...) :email "accounting@boots.rs"}
(...)
:smtp {:host "smtp.gmail.com"
:user "mr.blobby@buty.sa"
:pass "asd;l;kjsdfkljld"
:ssl true}}]
The :email
value can be a string (i.e. a single email address) or a list of strings.
Callbacks
A list of additional commands can be added to each invoice. Each command will be called with the generated invoice as its final parameter, e.g.
{:seller {...}
:buyer {...}
:items [...]
:callbacks [["ls" "-l"] ["rm"] ["du" "-sh"]]}
Will call the following commands (assuming that the generated invoice is /path/to/file.pdf
):
ls -l /path/to/file.pdf
du -sh /path/to/file.pdf
rm /path/to/file.pdf
The last one will obviously fail, as the file no longer exists, and the error message will be displayed