invoices/README.md
2021-08-03 13:45:56 +02:00

212 lines
10 KiB
Markdown

# 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`](https://github.com/mruwnik/invoices/blob/master/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](https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-basic-authentication/)
and [Tempo's](https://tempo-io.atlassian.net/wiki/spaces/KB/pages/199065601/How+to+use+Tempo+Cloud+REST+APIs)
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