# 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 ` == / 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